Анализ параметров в сложных распределениях

library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(ggplot2)
library(foreach)
library(doSNOW)
Loading required package: iterators
Loading required package: snow
library(plotly)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
library(latex2exp)

Attaching package: ‘latex2exp’

The following object is masked from ‘package:plotly’:

    TeX

Рассеяние

e_culc <- function(samp){
  var(samp) * (length(samp) - 1) / length(samp) / mean(samp)
}

Логарифмическое распределение

Табличная функция распредления логарифмического распределения:

log_prepering <- function(p = 0.5){
  tdistr <- (-p) / log(1 - p)
  sum_distr <- tdistr
  sum <- c(sum_distr)
  k <- 2
  
  while(tdistr > 1e-10){
    tdistr <- tdistr * p / k * (k - 1)
    sum_distr <- sum_distr + tdistr
    sum <- c(sum, sum_distr)
    k <- k + 1
  }
  
  sum
}

Моделирование логарифмического распределения по табличной функции распределения:

# rlog <- function(sum){
#   x <- runif(1)
#   j <- 1
#   
#   while (x > sum[j]){
#     j <- j + 1
#   }
#   
#   j
# }

rlog <- function(sum){
  which.min(sum < runif(1))
}

Моделирование выборки логарифмического распределения:

rnlog <- function(n = 1, sum){
  v <- replicate(n, rlog(sum))
  
  if(n == 0)
    v <- 0;
  
  v
}

Смоделируем выборку и построим гистограмму:

v <- rnlog(10000, log_prepering(0.75))
hist(v[v < 9], probability = TRUE, breaks = seq(0.5, max(v) + 0.5, 1), xlim = c(0.5, 8.5), xlab = "Sample", main = "")

Логарифмическое распределение с нулевым значением случайной величины

Заведём вероятность в нуле:

p_0 <- 0.2

Построим таблицу вероятносте для нашего распределения:

log_0_prepering <- function(p = 0.5){
  tdistr <- (1 - p_0) * (-p) / log(1 - p)
  sum_distr <- p_0
  sum <- c(p_0)
  k <- 2
  
  while(tdistr > 1e-10){
    sum_distr <- sum_distr + tdistr
    sum <- c(sum, sum_distr)
    tdistr <- tdistr * p / k * (k - 1)
    k <- k + 1
  }
  
  sum
}

Функция моделирования одной логарифмической с нулём величины:

rlog_0 <- function(p = 0.5, sum){
  x <- runif(1)
  j <- 1
  
  while (x > sum[j]){
    j <- j + 1
  }
  
  j - 1
}

Моделирование нескольких случайных величин:

rnlog_0 <- function(n = 1, p = 0.5, sum){
  v <- replicate(n, rlog_0(p, sum))
  v
}

Смоделируем выборку:

table_log <- log_0_prepering(0.75)
v <- rnlog_0(10000, 0.75, table_log)
hist(v[v < 9], probability = TRUE, breaks = seq(-0.5, max(v) + 0.5, 1), xlim = c(-0.5, 8.5), xlab = "Выборка", ylab = "Вероятность", main = "")

Биномиально-логарифмическое распределение

Моделирование биномиально-логарифмического распределения:

rbinomlog <- function(num = 1, n = 1, p = 0.5, sumlog){
  res <- c()
  
  for(i in rbinom(num, n, p)){
    res <- c(res, sum(0, rnlog(i, sumlog)))
  }
  
  res
}

Получение чисел Стирлинга первого рода:

MakeNumStir <- function(data, n){
  for (i in 2:n){
    for (j in 2:n){
      if (i == j){
        data[i, j] <- 1 / gamma(i)
      } else {
        data[i, j] <- data[i - 1, j - 1] / (i - 1) + data[i - 1, j] * (i - 2) / (i - 1) 
      }
    }
  }
  
  data
}

nNumStir <- 600
NumStir <- as.data.frame(matrix(nrow = nNumStir, ncol = nNumStir))
for (i in 1:nNumStir){
  NumStir[1, i] <- 0
  NumStir[i, 1] <- 0
}
NumStir[1, 1] <- 1
NumStir <- MakeNumStir(NumStir, nNumStir)

Вероятности биномиально-логарифмического распределения:

get_prob_binomlog <- function(k, p = 0.5, n = 1, q = 0.5){
  res <- 0
  alpha <- -1 / log(1 - q)
  frac <- 1

  for (j in 0:k){
    res <- res + frac * NumStir[k + 1, j + 1] * (p * alpha) ^j * (1 - p) ^(k - j)
    frac <- frac * (n - j)
  }
  
  prob <- (1 - p) ^(n - k) * q ^k * res
  
  if (is.nan(prob) || is.infinite(prob) || prob < 1e-309){
    return(1e-309)
  }
  
  (1 - p) ^(n - k) * q ^k * res
}

Функция правдоподобия для бином-логарифма:

m_log_lik_binomlog_old <- function(x.in, p = 0.5, n = 1, q = 0.5){
  if (q <= 0 || q >= 1 || p <= 0 || p >= 1 || n <= 0){
    return(-log(0));
  }
  res <- 0

  for (i in x.in){
    res <- res + log(get_prob_binomlog(i, p, n, q))
  }

  -res
}

m_log_lik_binomlog <- function(x.in, p = 0.5, n = 1, q = 0.5){
  if (q <= 0 || q >= 1 || p <= 0 || p >= 1 || n <= 0){
    return(-log(0));
  }
  
  -sum(sapply(x.in, function(i) log(get_prob_binomlog(i, p, n, q))))
}

Логарифмически-биномиальное распределение

Моделирование логарифмически-биномиального распределения:

rlogbinom <- function(num = 1, sumlog, n = 1, p = 0.5){
  res <- c()
  
  for(i in rnlog(num, sumlog)){
    res <- c(res, sum(0, rbinom(i, n, p)))
  }
  
  res
}

Вероятность лог-бинома для \(k = 0, 1, 2, 3, 4\):

get_prob_logbinom_logbinom_0 <- function(q = 0.5, p = 0.5, n = 1){
  log(1 - q* (1 - p) ^n) / log(1 - q)
}

get_prob_logbinom_logbinom_1 <- function(q = 0.5, p = 0.5, n = 1){
  -q * p * n * (1 - p)^(n - 1) / ((1 - q* (1 - p) ^n) * log(1 - q))
}

get_prob_logbinom_logbinom_2 <- function(q = 0.5, p = 0.5, n = 1){
  1 / 2 * (-q * p ^2 * n * (1 - p)^(n - 2) / ((1 - q* (1 - p) ^n) * log(1 - q))) *
    (q * n * (1 - p)^(n) / ((1 - q* (1 - p) ^n)) + (n - 1))
}

get_prob_logbinom_logbinom_3 <- function(q = 0.5, p = 0.5, n = 1){
  1 / 6 * (-q * p ^3 * n * (1 - p)^(n - 3) / ((1 - q* (1 - p) ^n) * log(1 - q))) *
    (2 * q ^2 * n ^2 * (1 - p)^(2 * n) / ((1 - q* (1 - p) ^n) ^2) +
      3 * q * n * (n - 1) * (1 - p)^(n) / ((1 - q* (1 - p) ^n)) + (n - 1) * (n - 2))
}

get_prob_logbinom_logbinom_4 <- function(q = 0.5, p = 0.5, n = 1){
  1 / 24 * (-q * p ^4 * n * (1 - p)^(n - 4) / ((1 - q* (1 - p) ^n) * log(1 - q))) *
    (3 * q ^3 * n ^3 * (1 - p)^(3 * n) / ((1 - q* (1 - p) ^n) ^3) +
      12 * q ^2 * n ^2 * (n - 1) * (1 - p)^(2 * n) / ((1 - q* (1 - p) ^n) ^2) +
        q * (1 - p)^(n) / ((1 - q* (1 - p) ^n)) * (3 * n * (n - 1) ^2 + 4 * n * (n - 1) * (n - 2)) +
          (n - 1) * (n - 2) * (n - 3))
}

Вероятности логарифмически-биномиального распределения:

get_prob_logbinom <- function(k, q = 0.5, p = 0.5, n = 1){
  if (k == 0){
    get_prob_logbinom_logbinom_0(q, p, n)
  } else if (k == 1){
    get_prob_logbinom_logbinom_1(q, p, n)
  } else if (k == 2){
    get_prob_logbinom_logbinom_2(q, p, n)
  } else if (k == 3){
    get_prob_logbinom_logbinom_3(q, p, n)
  } else if (k == 4){
    get_prob_logbinom_logbinom_4(q, p, n)
  } else if (k >= 5){
    1 - sum(sapply(0:4, function(k) get_prob_logbinom(k, q, p, n)))
  }
}

Функция правдоподобия для лог-бинома:

m_log_lik_logbinom <- function(x.in, q = 0.5, p = 0.5, n = 1){
  if (q <= 0 || q >= 1 || p <= 0 || p >= 1 || n <= 0){
    return(-log(0))
  }
  res <- 0
  
  for (i in x.in){
    prob <- get_prob_logbinom(i, q, p, n)

    if (prob < 0 || prob > 1 || is.nan(prob))
      return(-log(0))

    res <- res + log(prob)
  }
  
  -res
}

Логарифмически-пуассоновское распределение

Моделирование логарифмически-пуассоновское распределения:

rlogpois <- function(num = 1, sumlog, lambda = 1){
  res <- c()
  
  for(i in rnlog(num, sumlog)){
    res <- c(res, sum(0, rpois(i, lambda)))
  }
  
  res
}

Числа для ЛПР:

MakeNumPois <- function(data, n){
  for (i in 2:n){
    for (j in 2:n){
      if (i == j + 1){
        # data[i, j] <- 1 / gamma(i)
        data[i, j] <- 1 / gamma(i + 1)
      } else {
        # data[i, j] <- data[i - 1, j - 1] / (i - 1) + data[i - 1, j] * (i - 2) / (i - 1) 
        data[i, j] <- j * data[i - 1, j] / i + (i - j) * data[i - 1, j - 1] / i
      }
    }
  }
  
  data
}

nNumPois <- 600
NumPois <- as.data.frame(matrix(nrow = nNumPois, ncol = nNumPois))
for (i in 1:nNumPois){
  NumPois[i, 1] <- 1 / gamma(i + 1)
  NumPois[1, i] <- 0
}
NumPois[1, 1] <- 1
NumPois <- MakeNumPois(NumPois, nNumPois)

Вероятности логарифмически-пуассоновского распределения:

get_prob_logpois <- function(k, q = 0.5, lambda = 1){
  res <- 0
  alpha <- -1 / log(1 - q)
  m <- q * exp(-lambda)
  
  if (k == 0){
    -alpha * log(1 - m)
  } else if (k == 1){
    alpha * lambda * m / (1 - m)
  } else {
    for (j in 1:(k - 1)){
      res <- res + NumPois[k, j] * m ** j
    }
    
    res * lambda ** k / (1 - m) ** k * alpha
  }
}
get_prob_logpois_fullprob <- function(k, q = 0.5, lambda = 1){
  res <- 0
  alpha <- -1 / log(1 - q)
  
  for (j in 1:1000){
    part <- q ** j / gamma(k + 1) * j ** (k - 1) * exp(-j * lambda)
    
    if (part == 0){
      break
    }
    
    res <- res + part
  }
  
  res * alpha * lambda ** k
}

\[ P(S _\tau = k) = \frac 1 {k !} \frac {\lambda ^k} {(1 - m) ^k} \sum \limits _{j = 1} ^{k - 1} t(k, j) m ^j, \quad k = 2, ... \]

\[ t(k, j) = j \cdot t (k - 1, j) + (k - j) \cdot t(k - 1, j - 1) \]

\[ \begin{aligned} \left(\frac {\lambda ^k} {(1 - m) ^k} \sum \limits _{j = 1} ^{k - 1} t(k, j) m ^j\right)' =& \frac {km\lambda ^{k + 1}} {(1 - m) ^{k + 1}} \sum \limits _{j = 1} ^{k - 1} t(k, j) m ^j + \frac {(1 - m)\lambda ^k} {(1 - m) ^{k + 1}} \sum \limits _{j = 1} ^{k - 1} t(k, j) \lambda j m ^j =\\ =& \frac {\lambda ^{k + 1}} {(1 - m) ^{k + 1}} \left(\sum \limits _{j = 1} ^{k - 1} t(k, j) k m ^{j + 1} + \sum \limits _{j = 1} ^{k - 1} t(k, j) j m ^j - \sum \limits _{j = 1} ^{k - 1} t(k, j) j m ^{j + 1}\right) =\\ =& \frac {\lambda ^{k + 1}} {(1 - m) ^{k + 1}} \left(\sum \limits _{j = 2} ^{k} \left(t(k, j) j +t(k, j - 1) (k - j + 1)\right) m ^j + t(k, 1) m\right) =\\ =& \frac {\lambda ^{k + 1}} {(1 - m) ^{k + 1}} \left(\sum \limits _{j = 2} ^{k} t(k + 1, j) m ^j + t(k + 1, 1) m\right) =\\ =& \frac {\lambda ^{k + 1}} {(1 - m) ^{k + 1}} \sum \limits _{j = 1} ^{k} t(k + 1, j) m ^j \end{aligned} \]

Вероятность лог-пуассона для \(k = 0, 1, 2, 3, 4\):

# get_prob_logpois_logpois_0 <- function(q = 0.5, lambda = 1){
#   log(1 - q * exp(-lambda)) / log(1 - q)
# }
# 
# get_prob_logpois_logpois_1 <- function(q = 0.5, lambda = 1){
#   -q * lambda * exp(-lambda) / ((1 - q* exp(-lambda)) * log(1 - q))
# }
# 
# get_prob_logpois_logpois_2 <- function(q = 0.5, lambda = 1){
#   1 / 2 * (-q * lambda ^ 2 * exp(-lambda) / ((1 - q* exp(-lambda)) ^ 2 * log(1 - q)))
# }
# 
# get_prob_logpois_logpois_3 <- function(q = 0.5, lambda = 1){
#   1 / 6 * (q * exp(-lambda) + 1) * (-q * lambda ^ 3 * exp(-lambda) / ((1 - q* exp(-lambda)) ^ 3 * log(1 - q)))
# }
# 
# get_prob_logpois_logpois_4 <- function(q = 0.5, lambda = 1){
#   1 / 24 * (q ** 2 * exp(-2 * lambda) + 4 * q * exp(-lambda) + 1) * (-q * lambda ^ 4 * exp(-lambda) / ((1 - q* exp(-lambda)) ^ 4 * log(1 - q)))
# }

Вероятности логарифмически-пуассоновского распределения:

# get_prob_logpois <- function(k, q = 0.5, lambda = 1){
#   if (k == 0){
#     get_prob_logpois_logpois_0(q, lambda)
#   } else if (k == 1){
#     get_prob_logpois_logpois_1(q, lambda)
#   } else if (k == 2){
#     get_prob_logpois_logpois_2(q, lambda)
#   } else if (k == 3){
#     get_prob_logpois_logpois_3(q, lambda)
#   } else if (k == 4){
#     get_prob_logpois_logpois_4(q, lambda)
#   } else if (k >= 5){
#     1 - sum(sapply(0:4, function(k) get_prob_logpois(k, q, lambda)))
#   }
# }

Гистограмма распределения:

samLPR <- rlogpois(10000, log_prepering(0.25), lambda = 3.45)
hist.default(samLPR, breaks = seq(-0.5, max(samLPR) + 0.5, 1), probability = TRUE)
points(x = 0:max(samLPR), y = sapply(0:max(samLPR), function(k) get_prob_logpois(k, 0.25, 3.45)))

probLBR1 <- sapply(0:max(samLPR), function(k) get_prob_logpois(k, 0.25, 3.45))
probLBR2 <- sapply(0:max(samLPR), function(k) get_prob_logpois_fullprob(k, 0.25, 3.45))

Функция правдоподобия для лог-бинома:

m_log_lik_logpois <- function(x.in, q = 0.5, lambda = 1){
  if (q <= 0 || q >= 1 || lambda <= 0){
    return(-log(0))
  }
  res <- 0
  
  for (i in x.in){
    prob <- get_prob_logpois(i, q, lambda)

    if (prob < 0 || prob > 1 || is.nan(prob))
      return(-log(0))

    res <- res + log(prob)
  }
  
  -res
}

Негативный бином

Функция правдоподобия негативного бинома:

m_log_lik_nbinom <- function(x.in, q = 0.5, n = 1){
  if (q <= 0 || q >= 1 || n <= 0){
    return(-log(0));
  }
  res <- 0
  
  for (i in x.in){
    prob <- dnbinom(i, n, q)
    
    if (prob < 0 || prob > 1 || is.nan(prob))
      return(-log(0))
    
    res <- res + log(prob)
  }
  
  -res
}

Свёртка негативных биномов

Моделирование свёртки отрицательно-биномиальных распределений:

rdoublenbinom <- function(num = 1, p_1 = 0.5, size_1= 1, p_2 = 0.5, size_2 = 1){
  rnbinom(num, size_1, p_1) + rnbinom(num, size_2, p_2)
}

Вероятности свёртки негативных биномов:

get_prob_doublenbinom <- function(k, p_1 = 0.5, size_1= 1, p_2 = 0.5, size_2 = 1){
  res <- 0
  
  for (m in 0:k){
    res <- res + choose(m + size_1 - 1, m) * choose(k - m + size_2 - 1, k - m) * ((1 - p_1) ** m) * ((1 - p_2) ** (k - m))
  }
  
  return(res * (p_1 ** size_1) * (p_2 ** size_2))
}

Функция правдоподобия свёртки негативных биномов:

m_log_lik_doublenbinom <- function(x.in, p_1 = 0.5, size_1= 1, p_2 = 0.5, size_2 = 1){
  if (p_1 < 0 | p_1 > 1 | p_2 < 0 | p_2 > 1 | size_1 <= 0 | size_2 <= 0)
    return(-log(0))
  
  res <- 0
  
  for (i in x.in){
    prob <- get_prob_doublenbinom(i, p_1, size_1, p_2, size_2)
    
    if (prob < 0 || prob > 1 || is.nan(prob))
      return(-log(0))
    
    res <- res + log(prob)
  }
  
  -res
}

Пуассоновское распределение

m_log_lik_pois <- function(x.in, lambda = 1){
  if (lambda < 0)
    return(-log(0))
  
  res <- 0
  
  for (i in x.in){
    prob <- dpois(i, lambda = lambda)
    
    if (prob < 0 || prob > 1 || is.nan(prob))
      return(-log(0))
    
    res <- res + log(prob)
  }
  
  -res
}

Поиск оптимальных параметров и проверка согласия распределения по \(\chi ^2\)

Создание выборки по частотам значений случайной величины:

generate_sample_old <- function(num_k){
  sam <- c()
  
  for (i in 1:length(num_k)){
    sam <- c(sam, rep.int(i - 1, num_k[i]))
  }
  
  sam
}

generate_sample <- function(num_k){
  rep.int(0:(length(num_k) - 1), num_k)
}

Статистика критерия \(\chi ^2\):

my_chisq_old <- function(exp_prob, prob){
  res <- 0
  
  for (i in 1:length(prob)){
    res <- res + (exp_prob[i] - prob[i])^2/prob[i]
  }
  
  res
}

my_chisq <- function(exp_prob, prob){
  sum((exp_prob - prob)^2 / prob)
}

Проверка гипотезы о соответствии теоритического распредления эмперическому:

```{r}
Error: attempt to use zero-length variable name

Радиобиологические данные

Функция правдоподобия для ЛБР и БЛР:

funcMP_3D <- function(data, distr = 'binomlog', inter_par1 = c(0.01, 0.99, 100), inter_par2 = c(1, 100, 10)){
  library(rgl)
  
  open3d()
  lines3d(c(0, 0), c(0, 0), c(0, 12), color = "gray");
  lines3d(c(0, 12), c(0, 0), c(0, 0), color = "gray");
  lines3d(c(0, 0), c(0, 12), c(0, 0), color = "gray");
  
  sam <- generate_sample(data)
  N <- length(sam)
  var_sam = var(sam) * (N - 1) / N
  mean_sam = mean(sam)
  x <- c()
  y <- c()
  z <- c()
  for (par1 in seq(inter_par1[1], inter_par1[2], (inter_par1[2] - inter_par1[1]) / inter_par1[3])){
    for (par2 in seq(inter_par2[1], inter_par2[2], (inter_par2[2] - inter_par2[1]) / inter_par2[3])){
      if (distr == 'binomlog'){
        value <- m_log_lik_binomlog(sam, p = (var_sam / mean_sam * log(1 - par1) * (1 - par1) - log(1 - par1)) / par1, n = par2, q = par1)
      } else if (distr == 'logbinom'){
        value <- m_log_lik_logbinom(sam, q = par1, p = - log(1 - par1) * (1 - par1) / par2 / par1 * mean_sam, n = par2)
      } else if (distr == 'logpois'){
        value <- m_log_lik_logpois(sam, q = par1, lambda = par2)
      }
      
      if (value != 0) {
        x <- c(x, par1)
        y <- c(y, par2)
        z <- c(z, value)
      }
    }
  }
  
  z <- z - min(z)
  
  if (distr == 'binomlog'){
    z <- sqrt(z) / 10
    points3d(10 * x[z < 12], y[z < 12] * 10 / inter_par2[2], z[z < 12], color ="black")
  } else if (distr == 'logbinom'){
    points3d(10 * x[z < 12], y[z < 12] * 10 / inter_par2[2], z[z < 12], color ="black")
  } else if (distr == 'logpois'){
    points3d(10 * x[z < 12], y[z < 12] * 10 / inter_par2[2], z[z < 12], color ="black")
  }
}

Проверка оценки для ЛБР:

samLBR <- rlogbinom(1000, log_prepering(0.4), 6, 0.3)
samLBR <- sapply(samLBR, function(x) if (x > 4){5} else {x})
histLBR <- hist(as.numeric(samLBR), probability = TRUE, breaks = seq(-0.5, max(samLBR) + 0.5, 1), xlim = c(-0.5, max(samLBR) + 0.5), plot = TRUE)

res <- hist_make(0, as.numeric(histLBR$counts), get_hist = TRUE, 'logbinom')

# funcMP_3D(histLBR$counts, distr = 'logbinom')
res
[1] 5.0000000 0.4352014 0.3511828 0.7987618
q_LBR <- c()
n_LBR <- c()
p_LBR <- c()
seq_LBR <- seq(100, 10000, 500)
samLBR <- rlogbinom(10000, log_prepering(0.4), 6, 0.3)
samLBR <- sapply(samLBR, function(x) if (x > 4){5} else {x})

for (i in seq_LBR){
  histLBR <- hist(as.numeric(samLBR[1:i]), breaks = seq(-0.5, max(samLBR) + 0.5, 1), plot = FALSE)
  res <- hist_make(0, as.numeric(histLBR$counts), get_hist = FALSE, 'logbinom')
  n_LBR <- c(n_LBR, res[1])
  q_LBR <- c(q_LBR, res[2])
  p_LBR <- c(p_LBR, res[3])
}

Проверка оценки для БЛР:

q_BLR <- c()
n_BLR <- c()
p_BLR <- c()
seq_BLR <- seq(100, 10000, 500)
samBLR <- rbinomlog(10000, 6, 0.3, log_prepering(0.4))

for (i in seq_BLR){
  histBLR <- hist(as.numeric(samBLR[1:i]), breaks = seq(-0.5, max(samBLR) + 0.5, 1), plot = FALSE)
  res <- hist_make(0, as.numeric(histBLR$counts), get_hist = FALSE, 'binomlog')
  n_BLR <- c(n_BLR, res[1])
  q_BLR <- c(q_BLR, res[2])
  p_BLR <- c(p_BLR, res[3])
  print(i)
}
[1] 100
[1] 600
[1] 1100
[1] 1600
[1] 2100
[1] 2600
[1] 3100
[1] 3600
[1] 4100
[1] 4600
[1] 5100
[1] 5600
[1] 6100
[1] 6600
[1] 7100
[1] 7600
[1] 8100
[1] 8600
[1] 9100
[1] 9600
par(mfrow = c(1, 3))
plot(x = seq_BLR, y = q_BLR, xlab = "Размер выборки", ylab = "Оценка параметра q логарифма")
abline(h = 0.4, lty = "dashed")
legend(x = "bottomright", legend = "Истинное значение", lty = 2, col = 1, lwd = 2)
plot(x = seq_BLR, y = n_BLR, xlab = "Размер выборки", ylab = "Оценка параметра n бинома")
abline(h = 6, lty = "dashed")
legend(x = "topright", legend = "Истинное значение", lty = 2, col = 1, lwd = 2)
plot(x = seq_BLR, y = p_BLR, xlab = "Размер выборки", ylab = "Оценка параметра p бинома")
abline(h = 0.3, lty = "dashed")
legend(x = "bottomright", legend = "Истинное значение", lty = 2, col = 1, lwd = 2)
par(mfrow = c(1, 1))

Проверка оценки для ЛПР:

# funcMP_3D(histLPR$counts, distr = 'logpois', inter_par1 = c(0.01, 0.99, 100), inter_par2 = c(3.1, 4.1, 10))
res
[1] 3.5960192 0.2615519 0.1376612

Состоятельность:

q_LPR <- c()
lambda_LPR <- c()
samLPR <- rlogpois(10000, log_prepering(0.25), 3.45)

for (i in seq(100, 10000, 100)){
  histLPR <- hist(as.numeric(samLPR[1:i]), breaks = seq(-0.5, max(samLPR) + 0.5, 1), plot = FALSE)
  res <- hist_make(0, as.numeric(histLPR$counts), get_hist = FALSE, 'logpois')
  lambda_LPR <- c(lambda_LPR, res[1])
  q_LPR <- c(q_LPR, res[2])
}
plot(x = seq(100, 10000, 100), y = q_LPR, xlab = "Размер выборки", ylab = "Оценка параметра логарифма")
abline(h = 0.25, lty = "dashed")
legend(x = "bottomright", legend = "Истинное значение", lty = 2, col = 1, lwd = 2)

plot(x = seq(100, 10000, 100), y = lambda_LPR, xlab = "Размер выборки", ylab = "Оценка параметра Пуассона")
abline(h = 3.45, lty = "dashed")
legend(x = "bottomright", legend = "Истинное значение", lty = 2, col = 1, lwd = 2)

samNBR <- rnbinom(100, 3, 0.4)
samNBR <- sapply(samNBR, function(x) if (x > 4){5} else {x})
histLPR <- hist(as.numeric(samNBR), probability = TRUE, breaks = seq(-0.5, max(samNBR) + 0.5, 1), xlim = c(-0.5, max(samNBR) + 0.5), plot = TRUE)
res <- hist_make(0, as.numeric(histLPR$counts), get_hist = TRUE, 'nbinom')
funcMP_3D(histLPR$counts, distr = '', inter_par1 = c(0.01, 0.99, 100), inter_par2 = c(0.01, 2.99, 100))
res

Загрузка радиобиологических данных:

m <- read.csv("./VitroVivo.csv")
df.BLR <- data.frame(n = rep(0, 19), q = rep(0, 19), p = rep(0, 19), p_value = rep(0, 19))
df.LBR <- data.frame(n = rep(0, 19), q = rep(0, 19), p = rep(0, 19), p_value = rep(0, 19))
df.LPR <- data.frame(lambda = rep(0, 19), q = rep(0, 19), p_value = rep(0, 19))
df.NBR <- data.frame(n = rep(0, 19), q = rep(0, 19), p_value = rep(0, 19))

Рассеяние:

e.df <- data.frame(e = sapply(1:19, function(i) e_culc(generate_sample(m[i, ]))))
e.df

Нахождение \(n, q, p\) для которых p-value максимально:

for (i in 1:19){
  print(i)
  df.BLR[i, ] <- hist_make(0, as.numeric(m[i,]), get_hist = FALSE, 'binomlog')
  # df.LBR[i, ] <- hist_make(0, as.numeric(m[i,]), get_hist = FALSE, 'logbinom')
  # df.LPR[i, ] <- hist_make(0, as.numeric(m[i,]), get_hist = FALSE, 'logpois')
  # df.NBR[i, ] <- hist_make(0, as.numeric(m[i,]), get_hist = FALSE, 'nbinom')
  # df.PR[i, ] <- hist_make(0, as.numeric(m[i,]), get_hist = FALSE, 'pois')
}
[1] 1
Error in optim(c(2, 1/nLeft), function(x) m_log_lik_binomlog(sam, p = x[2],  : 
  function cannot be evaluated at initial parameters

Найденные оценки и p-value:

df.BLR
df.LPR <- df.LPR |> mutate(mean_log = -q / log(1 - q) / (1 - q))
df.LPR
df.NBR <- df.NBR |> mutate(lambda = -n / log(1 - q))
df.NBR

Гистограммы для строки данных:

Функция правдоподобия:

funcMP_3D(as.numeric(m[11,]), distr = 'logpois', inter_par1 = c(0.01, 0.99, 100), inter_par2 = c(0.01, 2.99, 100))
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Проверка на стабильность модели:

plot(x = 1:50, y = seq(0, 1, length.out = 50), col = "white")
for (j in 11:19){
  n <- 1:50
  res <- c()
  for (i in n){
    if (j <= 10){
      res <- c(res, hist_make(i, as.numeric(m[j,]), get_hist = FALSE, 'binomlog')[4])
    }
    else{
      res <- c(res, hist_make(i, as.numeric(m[j,]), get_hist = FALSE, 'logbinom')[4])
    }
  }
  lines(x = n, y = res, col = j)
}

Корреляция с радиацией параметров и среднего:

plot(seq(0, 45, 5), df.BLR$p[1:10], type = "l", col = "blue", ylim = c(0, max(df.BLR$p)), ylab = "Parametr p", xlab = "Dose, Gy", main = "Black - in vitro; Blue - in vivo")
lines(seq(0, 40, 5), df.BLR$p[11:19], type = "b", col = "black")


plot(seq(0, 45, 5), df.BLR$q[1:10], type = "l", col = "blue", ylim = c(0, max(df.BLR$q)), ylab = "Parametr q", xlab = "Dose, Gy", main = "Black - in vitro; Blue - in vivo")
lines(seq(0, 40, 5), df.BLR$q[11:19], type = "b", col = "black")


plot(seq(0, 45, 5), df.BLR$n[1:10], type = "l", ylim = c(0, max(df.BLR)), ylab = "Параметр n", xlab = "Доза облучения, Гр")
lines(seq(0, 40, 5), df.BLR$n[11:19], lty = 2)
legend(x = "bottomright", legend = c("in vivo", "in vitro"), lty = c(1, 2), col = 1, lwd = 2)

plot(seq(0, 45, 5), df.LBR$p[1:10], type = "l", col = "blue", ylim = c(0, max(df.LBR$p)), ylab = "Parametr p", xlab = "Dose, Gy", main = "Black - in vitro; Blue - in vivo")
lines(seq(0, 40, 5), df.LBR$p[11:19], type = "b", col = "black")


plot(seq(0, 45, 5), df.LBR$q[1:10], type = "l", col = "blue", ylim = c(0, max(df.LBR$q)), ylab = "Parametr q", xlab = "Dose, Gy", main = "Black - in vitro; Blue - in vivo")
lines(seq(0, 40, 5), df.LBR$q[11:19], type = "b", col = "black")


plot(seq(0, 45, 5), df.LBR$n[1:10], type = "l", col = "blue", ylim = c(0, max(df.LBR)), ylab = "Parametr n", xlab = "Dose, Gy", main = "Black - in vitro; Blue - in vivo")
lines(seq(0, 40, 5), df.LBR$n[11:19], type = "b", col = "black")

plot(seq(0, 45, 5), df.LPR$mean_log[1:10], type = "l", ylim = c(0.9, max(c(df.LPR$mean_log[1:10], df.LPR$mean_log[11:19]))), ylab = "Среднее логарифмического", xlab = "Доза облучения, Гр")
lines(seq(0, 40, 5), df.LPR$mean_log[11:19], lty = 2)
legend(x = "topright", legend = c("in vivo", "in vitro"), lty = c(1, 2), col = 1, lwd = 2)


plot(seq(0, 45, 5), df.LPR$lambda[1:10], type = "l", ylim = c(0, max(df.LPR$lambda)), ylab = "Среднее пуассоновского", xlab = "Доза облучения, Гр")
lines(seq(0, 40, 5), df.LPR$lambda[11:19], lty = 2)
legend(x = "bottomright", legend = c("in vivo", "in vitro"), lty = c(1, 2), col = 1, lwd = 2)

plot(seq(0, 45, 5), df.NB$q[1:10], type = "l", col = "blue", ylim = c(0, max(df.NB$q)), ylab = "Parametr q", xlab = "Dose, Gy", main = "Black - in vitro; Blue - in vivo")
lines(seq(0, 40, 5), df.NB$q[11:19], type = "b", col = "black")


plot(seq(0, 45, 5), df.NB$n[1:10], type = "l", col = "blue", ylim = c(0, max(df.NB$n)), ylab = "Parametr n", xlab = "Dose, Gy", main = "Black - in vitro; Blue - in vivo")
lines(seq(0, 40, 5), df.NB$n[11:19], type = "b", col = "black")


plot(seq(0, 45, 5), df.NBR$lambda[1:10], type = "l", col = "blue", ylim = c(0, max(df.NBR$lambda[1:10])), ylab = "Parametr lambda", xlab = "Dose, Gy", main = "Black - in vitro; Blue - in vivo")
lines(seq(0, 40, 5), df.NBR$lambda[11:19], type = "b", col = "black")

Анализ текстов

Загрузим данные:

Негативный бином

Рассчитаем параметры для первых \(1000\) слов:

df.word.nbinom <- data.frame()

start_time <- Sys.time()

cl <- makeCluster(5, type = "SOCK")
registerDoSNOW(cl)

df.word.nbinom <- foreach(i = 1:num.word, .combine = rbind, .inorder = TRUE) %dopar% {
  histWord <- hist(as.numeric(WordSample[i, ]), probability = TRUE, breaks = seq(-0.5, max(WordSample[i, ]) + 0.5, 1), xlim = c(-0.5, max(WordSample[i, ]) + 0.5), plot = FALSE)
  res <- hist_make(0, histWord$counts, get_hist = FALSE, distr = 'nbinom')
  c(row.names(WordSample)[i], res)
}

colnames(df.word.nbinom) <- c("name", "n", "q", "p_value")
df.word.nbinom <- as.data.frame(df.word.nbinom)
df.word.nbinom <- df.word.nbinom |> mutate(n = as.numeric(n), q = as.numeric(q), p_value = as.numeric(p_value))

timediff <- difftime(Sys.time(),start_time)
cat("Расчёт занял: ", timediff, units(timediff))

Точечный график параметров для полученных слов:

df.word.nbinom |> filter(n < 20) |> mutate(p_value_group = as.factor(ifelse(p_value < 0.1, 1, 2))) |>
  ggplot(aes(x = q, y = n, color = p_value_group)) +
  geom_point()

Биномиально-логарифмическое распределение

Рассчитаем параметры для первых \(1000\) слов:

df.word.binlog <- data.frame()

start_time <- Sys.time()

cl <- makeCluster(5, type = "SOCK")
registerDoSNOW(cl)

df.word.binlog <- foreach(i = 1:num.word, .combine = rbind, .inorder = TRUE) %dopar% {
  print(i)
  histWord <- hist(as.numeric(WordSample[i, ]), probability = TRUE, breaks = seq(-0.5, max(WordSample[i, ]) + 0.5, 1), xlim = c(-0.5, max(WordSample[i, ]) + 0.5), plot = FALSE)
  res <- hist_make(0, histWord$counts, get_hist = FALSE, distr = 'binomlog')
  c(row.names(WordSample)[i], res)
}

colnames(df.word.binlog) <- c("name", "n", "q", "p", "p_value")
df.word.binlog <- as.data.frame(df.word.binlog)
df.word.binlog <- df.word.binlog |> mutate(n = as.numeric(n), q = as.numeric(q), p = as.numeric(p), p_value = as.numeric(p_value))

timediff <- difftime(Sys.time(),start_time)
cat("Расчёт занял: ", timediff, units(timediff))
Расчёт занял:  1.572357 hours

Свёртка двух отрицательных биномов

Проверка оценки для свёртки отрицательных биномов:

samDNB <- rdoublenbinom(1000, 0.8, 3, 0.5, 10)
histDNB <- hist(as.numeric(samDNB), probability = TRUE, breaks = seq(-0.5, max(samDNB) + 0.5, 1), xlim = c(-0.5, max(samDNB) + 0.5), plot = TRUE)
res <- hist_make(0, as.numeric(histDNB$counts), get_hist = TRUE, 'doublenbinom')
res

Рассчитаем параметры для первых \(1000\) слов:

df.word.doublenbinom <- data.frame()

start_time <- Sys.time()

cl <- makeCluster(5, type = "SOCK")
registerDoSNOW(cl)

df.word.doublenbinom <- foreach(i = 1:num.word, .combine = rbind, .inorder = TRUE) %dopar% {
  histWord <- hist(as.numeric(WordSample[i, ]), probability = TRUE, breaks = seq(-0.5, max(WordSample[i, ]) + 0.5, 1), xlim = c(-0.5, max(WordSample[i, ]) + 0.5), plot = FALSE)
  res <- hist_make(0, histWord$counts, get_hist = FALSE, distr = 'doublenbinom')
  c(row.names(WordSample)[i], res)
}

colnames(df.word.doublenbinom) <- c("name", "p1", "size1", "p2", "size2", "p_value")
df.word.doublenbinom <- as.data.frame(df.word.doublenbinom)
df.word.doublenbinom <- df.word.doublenbinom |> mutate(p1 = as.numeric(p1), size1 = as.numeric(size1), p2 = as.numeric(p2), size2 = as.numeric(size2), p_value = as.numeric(p_value))

timediff <- difftime(Sys.time(),start_time)
cat("Расчёт занял: ", timediff, units(timediff))

Логарифмически-пуассоновское распределение

Рассчитаем параметры для первых \(1000\) слов:

df.word.logpois <- data.frame()

start_time <- Sys.time()

cl <- makeCluster(5, type = "SOCK")
registerDoSNOW(cl)

df.word.logpois <- foreach(i = 1:num.word, .combine = rbind, .inorder = TRUE) %dopar% {
  histWord <- hist(as.numeric(WordSample[i, ]), probability = TRUE, breaks = seq(-0.5, max(WordSample[i, ]) + 0.5, 1), xlim = c(-0.5, max(WordSample[i, ]) + 0.5), plot = FALSE)
  res <- hist_make(0, histWord$counts, get_hist = FALSE, distr = 'logpois')
  c(row.names(WordSample)[i], res)
}

colnames(df.word.logpois) <- c("name", "lambda", "q", "p_value")
df.word.logpois <- as.data.frame(df.word.logpois)
df.word.logpois <- df.word.logpois |> mutate(lambda = as.numeric(lambda), q = as.numeric(q), p_value = as.numeric(p_value))

timediff <- difftime(Sys.time(),start_time)
cat("Расчёт занял: ", timediff, units(timediff))
Расчёт занял:  2.395301 mins

Модификация Yule-Simon

Моделирование отрицательного бинома с параметром prob, распределённом по логарифму:

r_yule_simon_nbinom <- function(n, ro, size){
  res <- c()
  
  for (i in rnlog(n, log_prepering(ro))){
    res <- c(res, rnbinom(1, size, exp(-i)))
  }
  
  res
}

Вероятности этого распределения и гистограмма:

get_prob_yule_simon_nbinom <- function(k, q = 0.5, n = 1, max_num = 1000){
  res <- 0
  
  for(i in 1:max_num){
    nb <- dnbinom(k, n, exp(-i))
    if (is.nan(nb) | nb == 0){
      break
    }
    res <- res + nb * (-q ** i / log(1 - q) / i)
  }
  
  res
}

yule_simon.q <- 0.707
yule_simon.n <- 20.99
samp.yule_simon_nbinom <- r_yule_simon_nbinom(10000, yule_simon.q, yule_simon.n)
samp.yule_simon_nbinom <- samp.yule_simon_nbinom[samp.yule_simon_nbinom < 200]
hist.default(samp.yule_simon_nbinom, breaks = seq(-0.5, max(samp.yule_simon_nbinom) + 0.5, 1), probability = TRUE)
points(x = 0:200, y = sapply(0:200, function(n) get_prob_yule_simon_nbinom(n, yule_simon.q, yule_simon.n)))

Функция максимального правдоподобия:

m_log_lik_yule_simon_nbinom <- function(x.in, q = 0.5, n = 1){
  if (q <= 0 || q >= 1 || n <= 0){
    return(-log(0));
  }
  res <- 0
  
  for (i in x.in){
    prob <- get_prob_yule_simon_nbinom(i, q, n)
    
    if (prob < 0 || prob > 1 || is.nan(prob))
      return(-log(0))
    
    res <- res + log(prob)
  }
  
  -res
}

Рассчитаем параметры для первых \(1000\) слов:

df.word.yulesimonnbinom <- data.frame()

start_time <- Sys.time()

cl <- makeCluster(5, type = "SOCK")
registerDoSNOW(cl)

df.word.yulesimonnbinom <- foreach(i = 1:num.word, .combine = rbind, .inorder = TRUE) %dopar% {
  histWord <- hist(as.numeric(WordSample[i, ]), probability = TRUE, breaks = seq(-0.5, max(WordSample[i, ]) + 0.5, 1), xlim = c(-0.5, max(WordSample[i, ]) + 0.5), plot = FALSE)
  res <- hist_make(0, histWord$counts, get_hist = FALSE, distr = 'yulesimonnbinom')
  c(row.names(WordSample)[i], res)
}

colnames(df.word.yulesimonnbinom) <- c("name", "q", "n", "p_value")
df.word.yulesimonnbinom <- as.data.frame(df.word.yulesimonnbinom)
df.word.yulesimonnbinom <- df.word.yulesimonnbinom |> mutate(q = as.numeric(q), n = as.numeric(n), p_value = as.numeric(p_value))

timediff <- difftime(Sys.time(),start_time)
cat("Расчёт занял: ", timediff, units(timediff))
df.word.yulesimonnbinom <- df.word.yulesimonnbinom |> mutate(p_value = ifelse(p_value == 1, 0, p_value))

Разница между моделями

Гистограммы слова по всем расперделениям:

Сведём результаты в одну таблицу:

df.word.all <- cbind(df.word.nbinom, select(df.word.binlog, -name), select(df.word.doublenbinom, -name), freq = apply(WordSample, 1, sum)[1:1000])
colnames(df.word.all) <- c("name", "n_nbinom", "q_nbinom", "pvalue_nbinom", "n_binomlog", "q_binomlog", "p_binomlog", "pvalue_binomlog", "p1_doublenbinom", "size1_doublenbinom", "p2_doublenbinom", "size2_doublenbinom", "pvalue_doublenbinom", "freq")

Зададим уровень значимости:

alpha <- 0.05

Для каких слов все распределения хороши:

df.word.all |> select(name, pvalue_nbinom, pvalue_binomlog, pvalue_doublenbinom) |> filter(pvalue_nbinom > alpha & pvalue_binomlog > alpha & pvalue_doublenbinom > alpha)

Для каких слов лучше свёртка двух биномов:

df.word.all |> select(name, pvalue_nbinom, pvalue_binomlog, pvalue_doublenbinom) |> filter(pvalue_doublenbinom > alpha & pvalue_binomlog < alpha & pvalue_nbinom < alpha)

Для каких слов БЛР лучше:

df.word.all |> select(name, pvalue_nbinom, pvalue_binomlog, pvalue_doublenbinom) |> filter(pvalue_binomlog > alpha & pvalue_doublenbinom < alpha & pvalue_nbinom < alpha)

Для каких слов лучше негативный бином:

df.word.all |> select(name, pvalue_nbinom, pvalue_binomlog, pvalue_doublenbinom) |> filter(pvalue_nbinom > alpha & pvalue_doublenbinom < alpha & pvalue_binomlog < alpha)

Для каких слов лучше отрицательный бином и БЛР, но не свёртка:

df.word.all |> select(name, pvalue_nbinom, pvalue_binomlog, pvalue_doublenbinom) |> filter(pvalue_doublenbinom < alpha & pvalue_nbinom > alpha & pvalue_binomlog > alpha)

Для каких слов лучше отрицательный бином и свёртка, но не БЛР:

df.word.all |> select(name, pvalue_nbinom, pvalue_binomlog, pvalue_doublenbinom) |> filter(pvalue_doublenbinom > alpha & pvalue_nbinom > alpha & pvalue_binomlog < alpha)

Для каких слов лучше БЛР и свёртка, но не отрицательный бином:

df.word.all |> select(name, pvalue_nbinom, pvalue_binomlog, pvalue_doublenbinom) |> filter(pvalue_doublenbinom > alpha & pvalue_nbinom < alpha & pvalue_binomlog > alpha)

Для каких слов все плохи:

df.word.all |> select(name, pvalue_nbinom, pvalue_binomlog, pvalue_doublenbinom) |> filter(pvalue_nbinom < alpha & pvalue_doublenbinom < alpha & pvalue_binomlog < alpha)

Отдельные классы для слов:

df.word.all <- df.word.all |> 
  mutate(class = ifelse(pvalue_doublenbinom > alpha & pvalue_nbinom > alpha & pvalue_binomlog > alpha, "Все", "-")) |> 
  mutate(class = ifelse(pvalue_doublenbinom > alpha & pvalue_nbinom < alpha & pvalue_binomlog < alpha, "Сумма", class)) |> 
  mutate(class = ifelse(pvalue_doublenbinom < alpha & pvalue_nbinom < alpha & pvalue_binomlog > alpha, "БЛР", class)) |> 
  mutate(class = ifelse(pvalue_doublenbinom < alpha & pvalue_nbinom > alpha & pvalue_binomlog < alpha, "ОБР", class)) |>
  mutate(class = ifelse(pvalue_doublenbinom > alpha & pvalue_nbinom > alpha & pvalue_binomlog < alpha, "ОБР и сумма", class)) |>
  mutate(class = ifelse(pvalue_doublenbinom < alpha & pvalue_nbinom > alpha & pvalue_binomlog > alpha, "ОБР и БЛР", class)) |>
  mutate(class = ifelse(pvalue_doublenbinom > alpha & pvalue_nbinom < alpha & pvalue_binomlog > alpha, "БЛР и сумма", class)) |>
  mutate(class = ifelse(pvalue_doublenbinom < alpha & pvalue_nbinom < alpha & pvalue_binomlog < alpha, "Ни один", class))

Посмотрим на точечный график:

df.word.all |> plot_ly(x = ~log(n_nbinom), y = ~q_nbinom, z = ~log(freq), color = ~class, text = ~paste('word:', name), size = I(150), alpha = 1,  width = 1200, height = 700) |>
  layout(scene = list(xaxis = list(title = 'Логарифм параметра n негативного бинома'), yaxis = list(title = 'Параметр q негативного бинома'), zaxis = list(title = 'Логарифм количества')))
No trace type specified:
  Based on info supplied, a 'scatter3d' trace seems appropriate.
  Read more about this trace type -> https://plotly.com/r/reference/#scatter3d
No scatter3d mode specifed:
  Setting the mode to markers
  Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode
No trace type specified:
  Based on info supplied, a 'scatter3d' trace seems appropriate.
  Read more about this trace type -> https://plotly.com/r/reference/#scatter3d
No scatter3d mode specifed:
  Setting the mode to markers
  Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode
LS0tCnRpdGxlOiAi0JTQuNC/0LvQvtC8IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojINCQ0L3QsNC70LjQtyDQv9Cw0YDQsNC80LXRgtGA0L7QsiDQsiDRgdC70L7QttC90YvRhSDRgNCw0YHQv9GA0LXQtNC10LvQtdC90LjRj9GFCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZm9yZWFjaCkKbGlicmFyeShkb1NOT1cpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KGxhdGV4MmV4cCkKYGBgCgojIyDQoNCw0YHRgdC10Y/QvdC40LUKCmBgYHtyfQplX2N1bGMgPC0gZnVuY3Rpb24oc2FtcCl7CiAgdmFyKHNhbXApICogKGxlbmd0aChzYW1wKSAtIDEpIC8gbGVuZ3RoKHNhbXApIC8gbWVhbihzYW1wKQp9CmBgYAoKIyMg0JvQvtCz0LDRgNC40YTQvNC40YfQtdGB0LrQvtC1INGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNC1CgrQotCw0LHQu9C40YfQvdCw0Y8g0YTRg9C90LrRhtC40Y8g0YDQsNGB0L/RgNC10LTQu9C10L3QuNGPINC70L7Qs9Cw0YDQuNGE0LzQuNGH0LXRgdC60L7Qs9C+INGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNGPOgoKYGBge3J9CmxvZ19wcmVwZXJpbmcgPC0gZnVuY3Rpb24ocCA9IDAuNSl7CiAgdGRpc3RyIDwtICgtcCkgLyBsb2coMSAtIHApCiAgc3VtX2Rpc3RyIDwtIHRkaXN0cgogIHN1bSA8LSBjKHN1bV9kaXN0cikKICBrIDwtIDIKICAKICB3aGlsZSh0ZGlzdHIgPiAxZS0xMCl7CiAgICB0ZGlzdHIgPC0gdGRpc3RyICogcCAvIGsgKiAoayAtIDEpCiAgICBzdW1fZGlzdHIgPC0gc3VtX2Rpc3RyICsgdGRpc3RyCiAgICBzdW0gPC0gYyhzdW0sIHN1bV9kaXN0cikKICAgIGsgPC0gayArIDEKICB9CiAgCiAgc3VtCn0KYGBgCgrQnNC+0LTQtdC70LjRgNC+0LLQsNC90LjQtSDQu9C+0LPQsNGA0LjRhNC80LjRh9C10YHQutC+0LPQviDRgNCw0YHQv9GA0LXQtNC10LvQtdC90LjRjyDQv9C+INGC0LDQsdC70LjRh9C90L7QuSDRhNGD0L3QutGG0LjQuCDRgNCw0YHQv9GA0LXQtNC10LvQtdC90LjRjzoKCmBgYHtyfQojIHJsb2cgPC0gZnVuY3Rpb24oc3VtKXsKIyAgIHggPC0gcnVuaWYoMSkKIyAgIGogPC0gMQojICAgCiMgICB3aGlsZSAoeCA+IHN1bVtqXSl7CiMgICAgIGogPC0gaiArIDEKIyAgIH0KIyAgIAojICAgagojIH0KCnJsb2cgPC0gZnVuY3Rpb24oc3VtKXsKICB3aGljaC5taW4oc3VtIDwgcnVuaWYoMSkpCn0KYGBgCgrQnNC+0LTQtdC70LjRgNC+0LLQsNC90LjQtSDQstGL0LHQvtGA0LrQuCDQu9C+0LPQsNGA0LjRhNC80LjRh9C10YHQutC+0LPQviDRgNCw0YHQv9GA0LXQtNC10LvQtdC90LjRjzoKCmBgYHtyfQpybmxvZyA8LSBmdW5jdGlvbihuID0gMSwgc3VtKXsKICB2IDwtIHJlcGxpY2F0ZShuLCBybG9nKHN1bSkpCiAgCiAgaWYobiA9PSAwKQogICAgdiA8LSAwOwogIAogIHYKfQpgYGAKCtCh0LzQvtC00LXQu9C40YDRg9C10Lwg0LLRi9Cx0L7RgNC60YMg0Lgg0L/QvtGB0YLRgNC+0LjQvCDQs9C40YHRgtC+0LPRgNCw0LzQvNGDOgoKYGBge3J9CnYgPC0gcm5sb2coMTAwMDAsIGxvZ19wcmVwZXJpbmcoMC43NSkpCmhpc3Qodlt2IDwgOV0sIHByb2JhYmlsaXR5ID0gVFJVRSwgYnJlYWtzID0gc2VxKDAuNSwgbWF4KHYpICsgMC41LCAxKSwgeGxpbSA9IGMoMC41LCA4LjUpLCB4bGFiID0gIlNhbXBsZSIsIG1haW4gPSAiIikKYGBgCgojIyMg0JvQvtCz0LDRgNC40YTQvNC40YfQtdGB0LrQvtC1INGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNC1INGBINC90YPQu9C10LLRi9C8INC30L3QsNGH0LXQvdC40LXQvCDRgdC70YPRh9Cw0LnQvdC+0Lkg0LLQtdC70LjRh9C40L3RiwoK0JfQsNCy0LXQtNGR0Lwg0LLQtdGA0L7Rj9GC0L3QvtGB0YLRjCDQsiDQvdGD0LvQtToKCmBgYHtyfQpwXzAgPC0gMC4yCmBgYAoK0J/QvtGB0YLRgNC+0LjQvCDRgtCw0LHQu9C40YbRgyDQstC10YDQvtGP0YLQvdC+0YHRgtC1INC00LvRjyDQvdCw0YjQtdCz0L4g0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40Y86CgpgYGB7cn0KbG9nXzBfcHJlcGVyaW5nIDwtIGZ1bmN0aW9uKHAgPSAwLjUpewogIHRkaXN0ciA8LSAoMSAtIHBfMCkgKiAoLXApIC8gbG9nKDEgLSBwKQogIHN1bV9kaXN0ciA8LSBwXzAKICBzdW0gPC0gYyhwXzApCiAgayA8LSAyCiAgCiAgd2hpbGUodGRpc3RyID4gMWUtMTApewogICAgc3VtX2Rpc3RyIDwtIHN1bV9kaXN0ciArIHRkaXN0cgogICAgc3VtIDwtIGMoc3VtLCBzdW1fZGlzdHIpCiAgICB0ZGlzdHIgPC0gdGRpc3RyICogcCAvIGsgKiAoayAtIDEpCiAgICBrIDwtIGsgKyAxCiAgfQogIAogIHN1bQp9CmBgYAoK0KTRg9C90LrRhtC40Y8g0LzQvtC00LXQu9C40YDQvtCy0LDQvdC40Y8g0L7QtNC90L7QuSDQu9C+0LPQsNGA0LjRhNC80LjRh9C10YHQutC+0Lkg0YEg0L3Rg9C70ZHQvCDQstC10LvQuNGH0LjQvdGLOgoKYGBge3J9CnJsb2dfMCA8LSBmdW5jdGlvbihwID0gMC41LCBzdW0pewogIHggPC0gcnVuaWYoMSkKICBqIDwtIDEKICAKICB3aGlsZSAoeCA+IHN1bVtqXSl7CiAgICBqIDwtIGogKyAxCiAgfQogIAogIGogLSAxCn0KYGBgCgrQnNC+0LTQtdC70LjRgNC+0LLQsNC90LjQtSDQvdC10YHQutC+0LvRjNC60LjRhSDRgdC70YPRh9Cw0LnQvdGL0YUg0LLQtdC70LjRh9C40L06CgpgYGB7cn0Kcm5sb2dfMCA8LSBmdW5jdGlvbihuID0gMSwgcCA9IDAuNSwgc3VtKXsKICB2IDwtIHJlcGxpY2F0ZShuLCBybG9nXzAocCwgc3VtKSkKICB2Cn0KYGBgCgrQodC80L7QtNC10LvQuNGA0YPQtdC8INCy0YvQsdC+0YDQutGDOgoKYGBge3J9CnRhYmxlX2xvZyA8LSBsb2dfMF9wcmVwZXJpbmcoMC43NSkKdiA8LSBybmxvZ18wKDEwMDAwLCAwLjc1LCB0YWJsZV9sb2cpCmhpc3Qodlt2IDwgOV0sIHByb2JhYmlsaXR5ID0gVFJVRSwgYnJlYWtzID0gc2VxKC0wLjUsIG1heCh2KSArIDAuNSwgMSksIHhsaW0gPSBjKC0wLjUsIDguNSksIHhsYWIgPSAi0JLRi9Cx0L7RgNC60LAiLCB5bGFiID0gItCS0LXRgNC+0Y/RgtC90L7RgdGC0YwiLCBtYWluID0gIiIpCmBgYAoKIyMg0JHQuNC90L7QvNC40LDQu9GM0L3Qvi3Qu9C+0LPQsNGA0LjRhNC80LjRh9C10YHQutC+0LUg0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40LUKCtCc0L7QtNC10LvQuNGA0L7QstCw0L3QuNC1INCx0LjQvdC+0LzQuNCw0LvRjNC90L4t0LvQvtCz0LDRgNC40YTQvNC40YfQtdGB0LrQvtCz0L4g0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40Y86CgpgYGB7cn0KcmJpbm9tbG9nIDwtIGZ1bmN0aW9uKG51bSA9IDEsIG4gPSAxLCBwID0gMC41LCBzdW1sb2cpewogIHJlcyA8LSBjKCkKICAKICBmb3IoaSBpbiByYmlub20obnVtLCBuLCBwKSl7CiAgICByZXMgPC0gYyhyZXMsIHN1bSgwLCBybmxvZyhpLCBzdW1sb2cpKSkKICB9CiAgCiAgcmVzCn0KYGBgCgrQn9C+0LvRg9GH0LXQvdC40LUg0YfQuNGB0LXQuyDQodGC0LjRgNC70LjQvdCz0LAg0L/QtdGA0LLQvtCz0L4g0YDQvtC00LA6CgpgYGB7cn0KTWFrZU51bVN0aXIgPC0gZnVuY3Rpb24oZGF0YSwgbil7CiAgZm9yIChpIGluIDI6bil7CiAgICBmb3IgKGogaW4gMjpuKXsKICAgICAgaWYgKGkgPT0gail7CiAgICAgICAgZGF0YVtpLCBqXSA8LSAxIC8gZ2FtbWEoaSkKICAgICAgfSBlbHNlIHsKICAgICAgICBkYXRhW2ksIGpdIDwtIGRhdGFbaSAtIDEsIGogLSAxXSAvIChpIC0gMSkgKyBkYXRhW2kgLSAxLCBqXSAqIChpIC0gMikgLyAoaSAtIDEpIAogICAgICB9CiAgICB9CiAgfQogIAogIGRhdGEKfQoKbk51bVN0aXIgPC0gNjAwCk51bVN0aXIgPC0gYXMuZGF0YS5mcmFtZShtYXRyaXgobnJvdyA9IG5OdW1TdGlyLCBuY29sID0gbk51bVN0aXIpKQpmb3IgKGkgaW4gMTpuTnVtU3Rpcil7CiAgTnVtU3RpclsxLCBpXSA8LSAwCiAgTnVtU3RpcltpLCAxXSA8LSAwCn0KTnVtU3RpclsxLCAxXSA8LSAxCk51bVN0aXIgPC0gTWFrZU51bVN0aXIoTnVtU3Rpciwgbk51bVN0aXIpCmBgYAoK0JLQtdGA0L7Rj9GC0L3QvtGB0YLQuCDQsdC40L3QvtC80LjQsNC70YzQvdC+LdC70L7Qs9Cw0YDQuNGE0LzQuNGH0LXRgdC60L7Qs9C+INGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNGPOgoKYGBge3J9CmdldF9wcm9iX2Jpbm9tbG9nIDwtIGZ1bmN0aW9uKGssIHAgPSAwLjUsIG4gPSAxLCBxID0gMC41KXsKICByZXMgPC0gMAogIGFscGhhIDwtIC0xIC8gbG9nKDEgLSBxKQogIGZyYWMgPC0gMQoKICBmb3IgKGogaW4gMDprKXsKICAgIHJlcyA8LSByZXMgKyBmcmFjICogTnVtU3RpcltrICsgMSwgaiArIDFdICogKHAgKiBhbHBoYSkgXmogKiAoMSAtIHApIF4oayAtIGopCiAgICBmcmFjIDwtIGZyYWMgKiAobiAtIGopCiAgfQogIAogIHByb2IgPC0gKDEgLSBwKSBeKG4gLSBrKSAqIHEgXmsgKiByZXMKICAKICBpZiAoaXMubmFuKHByb2IpIHx8IGlzLmluZmluaXRlKHByb2IpIHx8IHByb2IgPCAxZS0zMDkpewogICAgcmV0dXJuKDFlLTMwOSkKICB9CiAgCiAgKDEgLSBwKSBeKG4gLSBrKSAqIHEgXmsgKiByZXMKfQoKZ2V0X3Byb2JfYmlub21sb2cgPC0gZnVuY3Rpb24oaywgcCA9IDAuNSwgbiA9IDEsIHEgPSAwLjUpewogIHJlcyA8LSAwCiAgYWxwaGEgPC0gLTEgLyBsb2coMSAtIHEpCiAgZnJhYyA8LSAxCgogIGZvciAoaiBpbiAwOmspewogICAgcmVzIDwtIHJlcyArIGZyYWMgKiBOdW1TdGlyW2sgKyAxLCBqICsgMV0gKiAocCAqIGFscGhhKSBeaiAqICgxIC0gcCkgXihrIC0gaikKICAgIGZyYWMgPC0gZnJhYyAqIChuIC0gaikKICB9CiAgCiAgcHJvYiA8LSAoMSAtIHApIF4obiAtIGspICogcSBeayAqIHJlcwogIAogIGlmIChpcy5uYW4ocHJvYikgfHwgaXMuaW5maW5pdGUocHJvYikgfHwgcHJvYiA8IDFlLTMwOSl7CiAgICByZXR1cm4oMWUtMzA5KQogIH0KICAKICAoMSAtIHApIF4obiAtIGspICogcSBeayAqIHJlcwp9CmBgYAoK0KTRg9C90LrRhtC40Y8g0L/RgNCw0LLQtNC+0L/QvtC00L7QsdC40Y8g0LTQu9GPINCx0LjQvdC+0Lwt0LvQvtCz0LDRgNC40YTQvNCwOgoKYGBge3J9Cm1fbG9nX2xpa19iaW5vbWxvZ19vbGQgPC0gZnVuY3Rpb24oeC5pbiwgcCA9IDAuNSwgbiA9IDEsIHEgPSAwLjUpewogIGlmIChxIDw9IDAgfHwgcSA+PSAxIHx8IHAgPD0gMCB8fCBwID49IDEgfHwgbiA8PSAwKXsKICAgIHJldHVybigtbG9nKDApKTsKICB9CiAgcmVzIDwtIDAKCiAgZm9yIChpIGluIHguaW4pewogICAgcmVzIDwtIHJlcyArIGxvZyhnZXRfcHJvYl9iaW5vbWxvZyhpLCBwLCBuLCBxKSkKICB9CgogIC1yZXMKfQoKbV9sb2dfbGlrX2Jpbm9tbG9nIDwtIGZ1bmN0aW9uKHguaW4sIHAgPSAwLjUsIG4gPSAxLCBxID0gMC41KXsKICBpZiAocSA8PSAwIHx8IHEgPj0gMSB8fCBwIDw9IDAgfHwgcCA+PSAxIHx8IG4gPD0gMCl7CiAgICByZXR1cm4oLWxvZygwKSk7CiAgfQogIAogIC1zdW0oc2FwcGx5KHguaW4sIGZ1bmN0aW9uKGkpIGxvZyhnZXRfcHJvYl9iaW5vbWxvZyhpLCBwLCBuLCBxKSkpKQp9CmBgYAoKIyMg0JvQvtCz0LDRgNC40YTQvNC40YfQtdGB0LrQuC3QsdC40L3QvtC80LjQsNC70YzQvdC+0LUg0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40LUKCtCc0L7QtNC10LvQuNGA0L7QstCw0L3QuNC1INC70L7Qs9Cw0YDQuNGE0LzQuNGH0LXRgdC60Lgt0LHQuNC90L7QvNC40LDQu9GM0L3QvtCz0L4g0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40Y86CgpgYGB7cn0KcmxvZ2Jpbm9tIDwtIGZ1bmN0aW9uKG51bSA9IDEsIHN1bWxvZywgbiA9IDEsIHAgPSAwLjUpewogIHJlcyA8LSBjKCkKICAKICBmb3IoaSBpbiBybmxvZyhudW0sIHN1bWxvZykpewogICAgcmVzIDwtIGMocmVzLCBzdW0oMCwgcmJpbm9tKGksIG4sIHApKSkKICB9CiAgCiAgcmVzCn0KYGBgCgrQktC10YDQvtGP0YLQvdC+0YHRgtGMINC70L7Qsy3QsdC40L3QvtC80LAg0LTQu9GPICRrID0gMCwgMSwgMiwgMywgNCQ6CgpgYGB7cn0KZ2V0X3Byb2JfbG9nYmlub21fbG9nYmlub21fMCA8LSBmdW5jdGlvbihxID0gMC41LCBwID0gMC41LCBuID0gMSl7CiAgbG9nKDEgLSBxKiAoMSAtIHApIF5uKSAvIGxvZygxIC0gcSkKfQoKZ2V0X3Byb2JfbG9nYmlub21fbG9nYmlub21fMSA8LSBmdW5jdGlvbihxID0gMC41LCBwID0gMC41LCBuID0gMSl7CiAgLXEgKiBwICogbiAqICgxIC0gcCleKG4gLSAxKSAvICgoMSAtIHEqICgxIC0gcCkgXm4pICogbG9nKDEgLSBxKSkKfQoKZ2V0X3Byb2JfbG9nYmlub21fbG9nYmlub21fMiA8LSBmdW5jdGlvbihxID0gMC41LCBwID0gMC41LCBuID0gMSl7CiAgMSAvIDIgKiAoLXEgKiBwIF4yICogbiAqICgxIC0gcCleKG4gLSAyKSAvICgoMSAtIHEqICgxIC0gcCkgXm4pICogbG9nKDEgLSBxKSkpICoKICAgIChxICogbiAqICgxIC0gcCleKG4pIC8gKCgxIC0gcSogKDEgLSBwKSBebikpICsgKG4gLSAxKSkKfQoKZ2V0X3Byb2JfbG9nYmlub21fbG9nYmlub21fMyA8LSBmdW5jdGlvbihxID0gMC41LCBwID0gMC41LCBuID0gMSl7CiAgMSAvIDYgKiAoLXEgKiBwIF4zICogbiAqICgxIC0gcCleKG4gLSAzKSAvICgoMSAtIHEqICgxIC0gcCkgXm4pICogbG9nKDEgLSBxKSkpICoKICAgICgyICogcSBeMiAqIG4gXjIgKiAoMSAtIHApXigyICogbikgLyAoKDEgLSBxKiAoMSAtIHApIF5uKSBeMikgKwogICAgICAzICogcSAqIG4gKiAobiAtIDEpICogKDEgLSBwKV4obikgLyAoKDEgLSBxKiAoMSAtIHApIF5uKSkgKyAobiAtIDEpICogKG4gLSAyKSkKfQoKZ2V0X3Byb2JfbG9nYmlub21fbG9nYmlub21fNCA8LSBmdW5jdGlvbihxID0gMC41LCBwID0gMC41LCBuID0gMSl7CiAgMSAvIDI0ICogKC1xICogcCBeNCAqIG4gKiAoMSAtIHApXihuIC0gNCkgLyAoKDEgLSBxKiAoMSAtIHApIF5uKSAqIGxvZygxIC0gcSkpKSAqCiAgICAoMyAqIHEgXjMgKiBuIF4zICogKDEgLSBwKV4oMyAqIG4pIC8gKCgxIC0gcSogKDEgLSBwKSBebikgXjMpICsKICAgICAgMTIgKiBxIF4yICogbiBeMiAqIChuIC0gMSkgKiAoMSAtIHApXigyICogbikgLyAoKDEgLSBxKiAoMSAtIHApIF5uKSBeMikgKwogICAgICAgIHEgKiAoMSAtIHApXihuKSAvICgoMSAtIHEqICgxIC0gcCkgXm4pKSAqICgzICogbiAqIChuIC0gMSkgXjIgKyA0ICogbiAqIChuIC0gMSkgKiAobiAtIDIpKSArCiAgICAgICAgICAobiAtIDEpICogKG4gLSAyKSAqIChuIC0gMykpCn0KYGBgCgrQktC10YDQvtGP0YLQvdC+0YHRgtC4INC70L7Qs9Cw0YDQuNGE0LzQuNGH0LXRgdC60Lgt0LHQuNC90L7QvNC40LDQu9GM0L3QvtCz0L4g0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40Y86CgpgYGB7cn0KZ2V0X3Byb2JfbG9nYmlub20gPC0gZnVuY3Rpb24oaywgcSA9IDAuNSwgcCA9IDAuNSwgbiA9IDEpewogIGlmIChrID09IDApewogICAgZ2V0X3Byb2JfbG9nYmlub21fbG9nYmlub21fMChxLCBwLCBuKQogIH0gZWxzZSBpZiAoayA9PSAxKXsKICAgIGdldF9wcm9iX2xvZ2Jpbm9tX2xvZ2Jpbm9tXzEocSwgcCwgbikKICB9IGVsc2UgaWYgKGsgPT0gMil7CiAgICBnZXRfcHJvYl9sb2diaW5vbV9sb2diaW5vbV8yKHEsIHAsIG4pCiAgfSBlbHNlIGlmIChrID09IDMpewogICAgZ2V0X3Byb2JfbG9nYmlub21fbG9nYmlub21fMyhxLCBwLCBuKQogIH0gZWxzZSBpZiAoayA9PSA0KXsKICAgIGdldF9wcm9iX2xvZ2Jpbm9tX2xvZ2Jpbm9tXzQocSwgcCwgbikKICB9IGVsc2UgaWYgKGsgPj0gNSl7CiAgICAxIC0gc3VtKHNhcHBseSgwOjQsIGZ1bmN0aW9uKGspIGdldF9wcm9iX2xvZ2Jpbm9tKGssIHEsIHAsIG4pKSkKICB9Cn0KYGBgCgrQpNGD0L3QutGG0LjRjyDQv9GA0LDQstC00L7Qv9C+0LTQvtCx0LjRjyDQtNC70Y8g0LvQvtCzLdCx0LjQvdC+0LzQsDoKCmBgYHtyfQptX2xvZ19saWtfbG9nYmlub20gPC0gZnVuY3Rpb24oeC5pbiwgcSA9IDAuNSwgcCA9IDAuNSwgbiA9IDEpewogIGlmIChxIDw9IDAgfHwgcSA+PSAxIHx8IHAgPD0gMCB8fCBwID49IDEgfHwgbiA8PSAwKXsKICAgIHJldHVybigtbG9nKDApKQogIH0KICByZXMgPC0gMAogIAogIGZvciAoaSBpbiB4LmluKXsKICAgIHByb2IgPC0gZ2V0X3Byb2JfbG9nYmlub20oaSwgcSwgcCwgbikKCiAgICBpZiAocHJvYiA8IDAgfHwgcHJvYiA+IDEgfHwgaXMubmFuKHByb2IpKQogICAgICByZXR1cm4oLWxvZygwKSkKCiAgICByZXMgPC0gcmVzICsgbG9nKHByb2IpCiAgfQogIAogIC1yZXMKfQpgYGAKCiMjINCb0L7Qs9Cw0YDQuNGE0LzQuNGH0LXRgdC60Lgt0L/Rg9Cw0YHRgdC+0L3QvtCy0YHQutC+0LUg0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40LUKCtCc0L7QtNC10LvQuNGA0L7QstCw0L3QuNC1INC70L7Qs9Cw0YDQuNGE0LzQuNGH0LXRgdC60Lgt0L/Rg9Cw0YHRgdC+0L3QvtCy0YHQutC+0LUg0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40Y86CgpgYGB7cn0KcmxvZ3BvaXMgPC0gZnVuY3Rpb24obnVtID0gMSwgc3VtbG9nLCBsYW1iZGEgPSAxKXsKICByZXMgPC0gYygpCiAgCiAgZm9yKGkgaW4gcm5sb2cobnVtLCBzdW1sb2cpKXsKICAgIHJlcyA8LSBjKHJlcywgc3VtKDAsIHJwb2lzKGksIGxhbWJkYSkpKQogIH0KICAKICByZXMKfQpgYGAKCtCn0LjRgdC70LAg0LTQu9GPINCb0J/QoDoKCmBgYHtyfQpNYWtlTnVtUG9pcyA8LSBmdW5jdGlvbihkYXRhLCBuKXsKICBmb3IgKGkgaW4gMjpuKXsKICAgIGZvciAoaiBpbiAyOm4pewogICAgICBpZiAoaSA9PSBqICsgMSl7CiAgICAgICAgIyBkYXRhW2ksIGpdIDwtIDEgLyBnYW1tYShpKQogICAgICAgIGRhdGFbaSwgal0gPC0gMSAvIGdhbW1hKGkgKyAxKQogICAgICB9IGVsc2UgewogICAgICAgICMgZGF0YVtpLCBqXSA8LSBkYXRhW2kgLSAxLCBqIC0gMV0gLyAoaSAtIDEpICsgZGF0YVtpIC0gMSwgal0gKiAoaSAtIDIpIC8gKGkgLSAxKSAKICAgICAgICBkYXRhW2ksIGpdIDwtIGogKiBkYXRhW2kgLSAxLCBqXSAvIGkgKyAoaSAtIGopICogZGF0YVtpIC0gMSwgaiAtIDFdIC8gaQogICAgICB9CiAgICB9CiAgfQogIAogIGRhdGEKfQoKbk51bVBvaXMgPC0gNjAwCk51bVBvaXMgPC0gYXMuZGF0YS5mcmFtZShtYXRyaXgobnJvdyA9IG5OdW1Qb2lzLCBuY29sID0gbk51bVBvaXMpKQpmb3IgKGkgaW4gMTpuTnVtUG9pcyl7CiAgTnVtUG9pc1tpLCAxXSA8LSAxIC8gZ2FtbWEoaSArIDEpCiAgTnVtUG9pc1sxLCBpXSA8LSAwCn0KTnVtUG9pc1sxLCAxXSA8LSAxCk51bVBvaXMgPC0gTWFrZU51bVBvaXMoTnVtUG9pcywgbk51bVBvaXMpCmBgYAoK0JLQtdGA0L7Rj9GC0L3QvtGB0YLQuCDQu9C+0LPQsNGA0LjRhNC80LjRh9C10YHQutC4LdC/0YPQsNGB0YHQvtC90L7QstGB0LrQvtCz0L4g0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40Y86CgpgYGB7cn0KZ2V0X3Byb2JfbG9ncG9pcyA8LSBmdW5jdGlvbihrLCBxID0gMC41LCBsYW1iZGEgPSAxKXsKICByZXMgPC0gMAogIGFscGhhIDwtIC0xIC8gbG9nKDEgLSBxKQogIG0gPC0gcSAqIGV4cCgtbGFtYmRhKQogIAogIGlmIChrID09IDApewogICAgLWFscGhhICogbG9nKDEgLSBtKQogIH0gZWxzZSBpZiAoayA9PSAxKXsKICAgIGFscGhhICogbGFtYmRhICogbSAvICgxIC0gbSkKICB9IGVsc2UgewogICAgZm9yIChqIGluIDE6KGsgLSAxKSl7CiAgICAgIHJlcyA8LSByZXMgKyBOdW1Qb2lzW2ssIGpdICogbSAqKiBqCiAgICB9CiAgICAKICAgIHJlcyAqIGxhbWJkYSAqKiBrIC8gKDEgLSBtKSAqKiBrICogYWxwaGEKICB9Cn0KYGBgCgpgYGB7cn0KZ2V0X3Byb2JfbG9ncG9pc19mdWxscHJvYiA8LSBmdW5jdGlvbihrLCBxID0gMC41LCBsYW1iZGEgPSAxKXsKICByZXMgPC0gMAogIGFscGhhIDwtIC0xIC8gbG9nKDEgLSBxKQogIAogIGZvciAoaiBpbiAxOjEwMDApewogICAgcGFydCA8LSBxICoqIGogLyBnYW1tYShrICsgMSkgKiBqICoqIChrIC0gMSkgKiBleHAoLWogKiBsYW1iZGEpCiAgICAKICAgIGlmIChwYXJ0ID09IDApewogICAgICBicmVhawogICAgfQogICAgCiAgICByZXMgPC0gcmVzICsgcGFydAogIH0KICAKICByZXMgKiBhbHBoYSAqIGxhbWJkYSAqKiBrCn0KYGBgCgokJApQKFMgX1x0YXUgPSBrKSA9IFxmcmFjIDEge2sgIX0gXGZyYWMge1xsYW1iZGEgXmt9IHsoMSAtIG0pIF5rfSBcc3VtIFxsaW1pdHMgX3tqID0gMX0gXntrIC0gMX0gdChrLCBqKSBtIF5qLCBccXVhZCBrID0gMiwgLi4uIAokJAoKJCQKdChrLCBqKSA9IGogXGNkb3QgdCAoayAtIDEsIGopICsgKGsgLSBqKSBcY2RvdCB0KGsgLSAxLCBqIC0gMSkKJCQKCiQkClxiZWdpbnthbGlnbmVkfQpcbGVmdChcZnJhYyB7XGxhbWJkYSBea30geygxIC0gbSkgXmt9IFxzdW0gXGxpbWl0cyBfe2ogPSAxfSBee2sgLSAxfSB0KGssIGopIG0gXmpccmlnaHQpJyA9JiBcZnJhYyB7a21cbGFtYmRhIF57ayArIDF9fSB7KDEgLSBtKSBee2sgKyAxfX0gXHN1bSBcbGltaXRzIF97aiA9IDF9IF57ayAtIDF9IHQoaywgaikgbSBeaiArIFxmcmFjIHsoMSAtIG0pXGxhbWJkYSBea30geygxIC0gbSkgXntrICsgMX19IFxzdW0gXGxpbWl0cyBfe2ogPSAxfSBee2sgLSAxfSB0KGssIGopIFxsYW1iZGEgaiBtIF5qID1cXAo9JiBcZnJhYyB7XGxhbWJkYSBee2sgKyAxfX0geygxIC0gbSkgXntrICsgMX19IFxsZWZ0KFxzdW0gXGxpbWl0cyBfe2ogPSAxfSBee2sgLSAxfSB0KGssIGopIGsgbSBee2ogKyAxfSArIFxzdW0gXGxpbWl0cyBfe2ogPSAxfSBee2sgLSAxfSB0KGssIGopIGogbSBeaiAtIFxzdW0gXGxpbWl0cyBfe2ogPSAxfSBee2sgLSAxfSB0KGssIGopIGogbSBee2ogKyAxfVxyaWdodCkgPVxcCj0mIFxmcmFjIHtcbGFtYmRhIF57ayArIDF9fSB7KDEgLSBtKSBee2sgKyAxfX0gXGxlZnQoXHN1bSBcbGltaXRzIF97aiA9IDJ9IF57a30gXGxlZnQodChrLCBqKSBqICt0KGssIGogLSAxKSAoayAtIGogKyAxKVxyaWdodCkgbSBeaiArIHQoaywgMSkgbVxyaWdodCkgPVxcCj0mIFxmcmFjIHtcbGFtYmRhIF57ayArIDF9fSB7KDEgLSBtKSBee2sgKyAxfX0gXGxlZnQoXHN1bSBcbGltaXRzIF97aiA9IDJ9IF57a30gdChrICsgMSwgaikgbSBeaiArIHQoayArIDEsIDEpIG1ccmlnaHQpID1cXAo9JiBcZnJhYyB7XGxhbWJkYSBee2sgKyAxfX0geygxIC0gbSkgXntrICsgMX19IFxzdW0gXGxpbWl0cyBfe2ogPSAxfSBee2t9IHQoayArIDEsIGopIG0gXmoKXGVuZHthbGlnbmVkfQokJAoK0JLQtdGA0L7Rj9GC0L3QvtGB0YLRjCDQu9C+0LMt0L/Rg9Cw0YHRgdC+0L3QsCDQtNC70Y8gJGsgPSAwLCAxLCAyLCAzLCA0JDoKCmBgYHtyfQojIGdldF9wcm9iX2xvZ3BvaXNfbG9ncG9pc18wIDwtIGZ1bmN0aW9uKHEgPSAwLjUsIGxhbWJkYSA9IDEpewojICAgbG9nKDEgLSBxICogZXhwKC1sYW1iZGEpKSAvIGxvZygxIC0gcSkKIyB9CiMgCiMgZ2V0X3Byb2JfbG9ncG9pc19sb2dwb2lzXzEgPC0gZnVuY3Rpb24ocSA9IDAuNSwgbGFtYmRhID0gMSl7CiMgICAtcSAqIGxhbWJkYSAqIGV4cCgtbGFtYmRhKSAvICgoMSAtIHEqIGV4cCgtbGFtYmRhKSkgKiBsb2coMSAtIHEpKQojIH0KIyAKIyBnZXRfcHJvYl9sb2dwb2lzX2xvZ3BvaXNfMiA8LSBmdW5jdGlvbihxID0gMC41LCBsYW1iZGEgPSAxKXsKIyAgIDEgLyAyICogKC1xICogbGFtYmRhIF4gMiAqIGV4cCgtbGFtYmRhKSAvICgoMSAtIHEqIGV4cCgtbGFtYmRhKSkgXiAyICogbG9nKDEgLSBxKSkpCiMgfQojIAojIGdldF9wcm9iX2xvZ3BvaXNfbG9ncG9pc18zIDwtIGZ1bmN0aW9uKHEgPSAwLjUsIGxhbWJkYSA9IDEpewojICAgMSAvIDYgKiAocSAqIGV4cCgtbGFtYmRhKSArIDEpICogKC1xICogbGFtYmRhIF4gMyAqIGV4cCgtbGFtYmRhKSAvICgoMSAtIHEqIGV4cCgtbGFtYmRhKSkgXiAzICogbG9nKDEgLSBxKSkpCiMgfQojIAojIGdldF9wcm9iX2xvZ3BvaXNfbG9ncG9pc180IDwtIGZ1bmN0aW9uKHEgPSAwLjUsIGxhbWJkYSA9IDEpewojICAgMSAvIDI0ICogKHEgKiogMiAqIGV4cCgtMiAqIGxhbWJkYSkgKyA0ICogcSAqIGV4cCgtbGFtYmRhKSArIDEpICogKC1xICogbGFtYmRhIF4gNCAqIGV4cCgtbGFtYmRhKSAvICgoMSAtIHEqIGV4cCgtbGFtYmRhKSkgXiA0ICogbG9nKDEgLSBxKSkpCiMgfQpgYGAKCtCS0LXRgNC+0Y/RgtC90L7RgdGC0Lgg0LvQvtCz0LDRgNC40YTQvNC40YfQtdGB0LrQuC3Qv9GD0LDRgdGB0L7QvdC+0LLRgdC60L7Qs9C+INGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNGPOgoKYGBge3J9CiMgZ2V0X3Byb2JfbG9ncG9pcyA8LSBmdW5jdGlvbihrLCBxID0gMC41LCBsYW1iZGEgPSAxKXsKIyAgIGlmIChrID09IDApewojICAgICBnZXRfcHJvYl9sb2dwb2lzX2xvZ3BvaXNfMChxLCBsYW1iZGEpCiMgICB9IGVsc2UgaWYgKGsgPT0gMSl7CiMgICAgIGdldF9wcm9iX2xvZ3BvaXNfbG9ncG9pc18xKHEsIGxhbWJkYSkKIyAgIH0gZWxzZSBpZiAoayA9PSAyKXsKIyAgICAgZ2V0X3Byb2JfbG9ncG9pc19sb2dwb2lzXzIocSwgbGFtYmRhKQojICAgfSBlbHNlIGlmIChrID09IDMpewojICAgICBnZXRfcHJvYl9sb2dwb2lzX2xvZ3BvaXNfMyhxLCBsYW1iZGEpCiMgICB9IGVsc2UgaWYgKGsgPT0gNCl7CiMgICAgIGdldF9wcm9iX2xvZ3BvaXNfbG9ncG9pc180KHEsIGxhbWJkYSkKIyAgIH0gZWxzZSBpZiAoayA+PSA1KXsKIyAgICAgMSAtIHN1bShzYXBwbHkoMDo0LCBmdW5jdGlvbihrKSBnZXRfcHJvYl9sb2dwb2lzKGssIHEsIGxhbWJkYSkpKQojICAgfQojIH0KYGBgCgrQk9C40YHRgtC+0LPRgNCw0LzQvNCwINGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNGPOgoKYGBge3J9CnNhbUxQUiA8LSBybG9ncG9pcygxMDAwMCwgbG9nX3ByZXBlcmluZygwLjI1KSwgbGFtYmRhID0gMy40NSkKaGlzdC5kZWZhdWx0KHNhbUxQUiwgYnJlYWtzID0gc2VxKC0wLjUsIG1heChzYW1MUFIpICsgMC41LCAxKSwgcHJvYmFiaWxpdHkgPSBUUlVFKQpwb2ludHMoeCA9IDA6bWF4KHNhbUxQUiksIHkgPSBzYXBwbHkoMDptYXgoc2FtTFBSKSwgZnVuY3Rpb24oaykgZ2V0X3Byb2JfbG9ncG9pcyhrLCAwLjI1LCAzLjQ1KSkpCnByb2JMQlIxIDwtIHNhcHBseSgwOm1heChzYW1MUFIpLCBmdW5jdGlvbihrKSBnZXRfcHJvYl9sb2dwb2lzKGssIDAuMjUsIDMuNDUpKQpwcm9iTEJSMiA8LSBzYXBwbHkoMDptYXgoc2FtTFBSKSwgZnVuY3Rpb24oaykgZ2V0X3Byb2JfbG9ncG9pc19mdWxscHJvYihrLCAwLjI1LCAzLjQ1KSkKYGBgCgrQpNGD0L3QutGG0LjRjyDQv9GA0LDQstC00L7Qv9C+0LTQvtCx0LjRjyDQtNC70Y8g0LvQvtCzLdCx0LjQvdC+0LzQsDoKCmBgYHtyfQptX2xvZ19saWtfbG9ncG9pcyA8LSBmdW5jdGlvbih4LmluLCBxID0gMC41LCBsYW1iZGEgPSAxKXsKICBpZiAocSA8PSAwIHx8IHEgPj0gMSB8fCBsYW1iZGEgPD0gMCl7CiAgICByZXR1cm4oLWxvZygwKSkKICB9CiAgcmVzIDwtIDAKICAKICBmb3IgKGkgaW4geC5pbil7CiAgICBwcm9iIDwtIGdldF9wcm9iX2xvZ3BvaXMoaSwgcSwgbGFtYmRhKQoKICAgIGlmIChwcm9iIDwgMCB8fCBwcm9iID4gMSB8fCBpcy5uYW4ocHJvYikpCiAgICAgIHJldHVybigtbG9nKDApKQoKICAgIHJlcyA8LSByZXMgKyBsb2cocHJvYikKICB9CiAgCiAgLXJlcwp9CmBgYAoKIyMg0J3QtdCz0LDRgtC40LLQvdGL0Lkg0LHQuNC90L7QvAoK0KTRg9C90LrRhtC40Y8g0L/RgNCw0LLQtNC+0L/QvtC00L7QsdC40Y8g0L3QtdCz0LDRgtC40LLQvdC+0LPQviDQsdC40L3QvtC80LA6CgpgYGB7cn0KbV9sb2dfbGlrX25iaW5vbSA8LSBmdW5jdGlvbih4LmluLCBxID0gMC41LCBuID0gMSl7CiAgaWYgKHEgPD0gMCB8fCBxID49IDEgfHwgbiA8PSAwKXsKICAgIHJldHVybigtbG9nKDApKTsKICB9CiAgcmVzIDwtIDAKICAKICBmb3IgKGkgaW4geC5pbil7CiAgICBwcm9iIDwtIGRuYmlub20oaSwgbiwgcSkKICAgIAogICAgaWYgKHByb2IgPCAwIHx8IHByb2IgPiAxIHx8IGlzLm5hbihwcm9iKSkKICAgICAgcmV0dXJuKC1sb2coMCkpCiAgICAKICAgIHJlcyA8LSByZXMgKyBsb2cocHJvYikKICB9CiAgCiAgLXJlcwp9CmBgYAoKIyMg0KHQstGR0YDRgtC60LAg0L3QtdCz0LDRgtC40LLQvdGL0YUg0LHQuNC90L7QvNC+0LIKCtCc0L7QtNC10LvQuNGA0L7QstCw0L3QuNC1INGB0LLRkdGA0YLQutC4INC+0YLRgNC40YbQsNGC0LXQu9GM0L3Qvi3QsdC40L3QvtC80LjQsNC70YzQvdGL0YUg0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40Lk6CgpgYGB7cn0KcmRvdWJsZW5iaW5vbSA8LSBmdW5jdGlvbihudW0gPSAxLCBwXzEgPSAwLjUsIHNpemVfMT0gMSwgcF8yID0gMC41LCBzaXplXzIgPSAxKXsKICBybmJpbm9tKG51bSwgc2l6ZV8xLCBwXzEpICsgcm5iaW5vbShudW0sIHNpemVfMiwgcF8yKQp9CmBgYAoK0JLQtdGA0L7Rj9GC0L3QvtGB0YLQuCDRgdCy0ZHRgNGC0LrQuCDQvdC10LPQsNGC0LjQstC90YvRhSDQsdC40L3QvtC80L7QsjoKCmBgYHtyfQpnZXRfcHJvYl9kb3VibGVuYmlub20gPC0gZnVuY3Rpb24oaywgcF8xID0gMC41LCBzaXplXzE9IDEsIHBfMiA9IDAuNSwgc2l6ZV8yID0gMSl7CiAgcmVzIDwtIDAKICAKICBmb3IgKG0gaW4gMDprKXsKICAgIHJlcyA8LSByZXMgKyBjaG9vc2UobSArIHNpemVfMSAtIDEsIG0pICogY2hvb3NlKGsgLSBtICsgc2l6ZV8yIC0gMSwgayAtIG0pICogKCgxIC0gcF8xKSAqKiBtKSAqICgoMSAtIHBfMikgKiogKGsgLSBtKSkKICB9CiAgCiAgcmV0dXJuKHJlcyAqIChwXzEgKiogc2l6ZV8xKSAqIChwXzIgKiogc2l6ZV8yKSkKfQpgYGAKCtCk0YPQvdC60YbQuNGPINC/0YDQsNCy0LTQvtC/0L7QtNC+0LHQuNGPINGB0LLRkdGA0YLQutC4INC90LXQs9Cw0YLQuNCy0L3Ri9GFINCx0LjQvdC+0LzQvtCyOgoKYGBge3J9Cm1fbG9nX2xpa19kb3VibGVuYmlub20gPC0gZnVuY3Rpb24oeC5pbiwgcF8xID0gMC41LCBzaXplXzE9IDEsIHBfMiA9IDAuNSwgc2l6ZV8yID0gMSl7CiAgaWYgKHBfMSA8IDAgfCBwXzEgPiAxIHwgcF8yIDwgMCB8IHBfMiA+IDEgfCBzaXplXzEgPD0gMCB8IHNpemVfMiA8PSAwKQogICAgcmV0dXJuKC1sb2coMCkpCiAgCiAgcmVzIDwtIDAKICAKICBmb3IgKGkgaW4geC5pbil7CiAgICBwcm9iIDwtIGdldF9wcm9iX2RvdWJsZW5iaW5vbShpLCBwXzEsIHNpemVfMSwgcF8yLCBzaXplXzIpCiAgICAKICAgIGlmIChwcm9iIDwgMCB8fCBwcm9iID4gMSB8fCBpcy5uYW4ocHJvYikpCiAgICAgIHJldHVybigtbG9nKDApKQogICAgCiAgICByZXMgPC0gcmVzICsgbG9nKHByb2IpCiAgfQogIAogIC1yZXMKfQpgYGAKCiMjINCf0YPQsNGB0YHQvtC90L7QstGB0LrQvtC1INGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNC1CgpgYGB7cn0KbV9sb2dfbGlrX3BvaXMgPC0gZnVuY3Rpb24oeC5pbiwgbGFtYmRhID0gMSl7CiAgaWYgKGxhbWJkYSA8IDApCiAgICByZXR1cm4oLWxvZygwKSkKICAKICByZXMgPC0gMAogIAogIGZvciAoaSBpbiB4LmluKXsKICAgIHByb2IgPC0gZHBvaXMoaSwgbGFtYmRhID0gbGFtYmRhKQogICAgCiAgICBpZiAocHJvYiA8IDAgfHwgcHJvYiA+IDEgfHwgaXMubmFuKHByb2IpKQogICAgICByZXR1cm4oLWxvZygwKSkKICAgIAogICAgcmVzIDwtIHJlcyArIGxvZyhwcm9iKQogIH0KICAKICAtcmVzCn0KYGBgCgojIyDQn9C+0LjRgdC6INC+0L/RgtC40LzQsNC70YzQvdGL0YUg0L/QsNGA0LDQvNC10YLRgNC+0LIg0Lgg0L/RgNC+0LLQtdGA0LrQsCDRgdC+0LPQu9Cw0YHQuNGPINGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNGPINC/0L4gJFxjaGkgXjIkCgrQodC+0LfQtNCw0L3QuNC1INCy0YvQsdC+0YDQutC4INC/0L4g0YfQsNGB0YLQvtGC0LDQvCDQt9C90LDRh9C10L3QuNC5INGB0LvRg9GH0LDQudC90L7QuSDQstC10LvQuNGH0LjQvdGLOgoKYGBge3J9CmdlbmVyYXRlX3NhbXBsZV9vbGQgPC0gZnVuY3Rpb24obnVtX2spewogIHNhbSA8LSBjKCkKICAKICBmb3IgKGkgaW4gMTpsZW5ndGgobnVtX2spKXsKICAgIHNhbSA8LSBjKHNhbSwgcmVwLmludChpIC0gMSwgbnVtX2tbaV0pKQogIH0KICAKICBzYW0KfQoKZ2VuZXJhdGVfc2FtcGxlIDwtIGZ1bmN0aW9uKG51bV9rKXsKICByZXAuaW50KDA6KGxlbmd0aChudW1faykgLSAxKSwgbnVtX2spCn0KYGBgCgrQodGC0LDRgtC40YHRgtC40LrQsCDQutGA0LjRgtC10YDQuNGPICRcY2hpIF4yJDoKCmBgYHtyfQpteV9jaGlzcV9vbGQgPC0gZnVuY3Rpb24oZXhwX3Byb2IsIHByb2IpewogIHJlcyA8LSAwCiAgCiAgZm9yIChpIGluIDE6bGVuZ3RoKHByb2IpKXsKICAgIHJlcyA8LSByZXMgKyAoZXhwX3Byb2JbaV0gLSBwcm9iW2ldKV4yL3Byb2JbaV0KICB9CiAgCiAgcmVzCn0KCm15X2NoaXNxIDwtIGZ1bmN0aW9uKGV4cF9wcm9iLCBwcm9iKXsKICBzdW0oKGV4cF9wcm9iIC0gcHJvYileMiAvIHByb2IpCn0KYGBgCgrQn9GA0L7QstC10YDQutCwINCz0LjQv9C+0YLQtdC30Ysg0L4g0YHQvtC+0YLQstC10YLRgdGC0LLQuNC4INGC0LXQvtGA0LjRgtC40YfQtdGB0LrQvtCz0L4g0YDQsNGB0L/RgNC10LTQu9C10L3QuNGPINGN0LzQv9C10YDQuNGH0LXRgdC60L7QvNGDOgoKYGBge3J9Cmhpc3RfbWFrZSA8LSBmdW5jdGlvbiAobiwgZXhwX3Byb2IsIGdldF9oaXN0LCBkaXN0ciA9ICdiaW5vbWxvZycpewogIHNhbSA8LSBnZW5lcmF0ZV9zYW1wbGUoZXhwX3Byb2IpCiAgTiA8LSBsZW5ndGgoc2FtKQogIHZhcl9zYW0gPC0gdmFyKHNhbSkgKiAoTiAtIDEpIC8gTgogIG1lYW5fc2FtIDwtIG1lYW4oc2FtKQogIGV4cF9wcm9iIDwtIGV4cF9wcm9iIC8gTgogIAogIGlmIChuID09IDApewogICAgZGYgPC0gLTEKICB9CiAgZWxzZXsKICAgIGRmIDwtIDAKICB9CgogIGlmIChkaXN0ciA9PSAnYmlub21sb2cnKXsKICAgIGlmIChuID09IDApewogICAgICBsZWZ0IDwtIDEKICAgICAgcmlnaHQgPC0gMTAwMAogICAgICB3aGlsZSAocmlnaHQgLSBsZWZ0ID4gMikgewogICAgICAgIG5MZWZ0IDwtICgyICogbGVmdCArIHJpZ2h0KSAlLyUgMwogICAgICAgIG5SaWdodCA8LSAobGVmdCArIDIgKiByaWdodCkgJS8lIDMKICAgICAgICAjIHFMZWZ0IDwtIG9wdGltaXplKGZ1bmN0aW9uKHgpIG1fbG9nX2xpa19iaW5vbWxvZyhzYW0sIHAgPSAodmFyX3NhbSAvIG1lYW5fc2FtICogbG9nKDEgLSB4KSAqICgxIC0geCkgLSBsb2coMSAtIHgpKSAvIHgsIG4gPSBuTGVmdCwgcSA9IHgpLCBjKDAuMDEsIDAuOTkpKQogICAgICAgICMgcVJpZ2h0IDwtIG9wdGltaXplKGZ1bmN0aW9uKHgpIG1fbG9nX2xpa19iaW5vbWxvZyhzYW0sIHAgPSAodmFyX3NhbSAvIG1lYW5fc2FtICogbG9nKDEgLSB4KSAqICgxIC0geCkgLSBsb2coMSAtIHgpKSAvIHgsIG4gPSBuUmlnaHQsIHEgPSB4KSwgYygwLjAxLCAwLjk5KSkKICAgICAgICAjIHByaW50KG5MZWZ0KQogICAgICAgIHFMZWZ0IDwtIG9wdGltKGMoMC41LCAxIC8gbkxlZnQpLCBmdW5jdGlvbih4KSBtX2xvZ19saWtfYmlub21sb2coc2FtLCBwID0geFsyXSwgbiA9IG5MZWZ0LCBxID0geFsxXSkpCiAgICAgICAgcVJpZ2h0IDwtIG9wdGltKGMoMC41LCAxIC8gblJpZ2h0KSwgZnVuY3Rpb24oeCkgbV9sb2dfbGlrX2Jpbm9tbG9nKHNhbSwgcCA9IHhbMl0sIG4gPSBuUmlnaHQsIHEgPSB4WzFdKSkKCiAgICAgICAgIyBpZiAocUxlZnQkb2JqZWN0aXZlIDw9IHFSaWdodCRvYmplY3RpdmUpIHsKICAgICAgICAjICAgcmlnaHQgPC0gblJpZ2h0CiAgICAgICAgIyB9CiAgICAgICAgIyBlbHNlewogICAgICAgICMgICBsZWZ0IDwtIG5MZWZ0CiAgICAgICAgIyB9CiAgICAgICAgaWYgKHFMZWZ0JHZhbHVlIDw9IHFSaWdodCR2YWx1ZSkgewogICAgICAgICAgcmlnaHQgPC0gblJpZ2h0CiAgICAgICAgfQogICAgICAgIGVsc2V7CiAgICAgICAgICBsZWZ0IDwtIG5MZWZ0CiAgICAgICAgfQogICAgICB9CiAgICAgICMgbWluVmFsdWUgPC0gb3B0aW1pemUoZnVuY3Rpb24oeCkgbV9sb2dfbGlrX2Jpbm9tbG9nKHNhbSwgcCA9ICh2YXJfc2FtIC8gbWVhbl9zYW0gKiBsb2coMSAtIHgpICogKDEgLSB4KSAtIGxvZygxIC0geCkpIC8geCwgbiA9IGxlZnQsIHEgPSB4KSwgYygwLjAxLCAwLjk5KSkKICAgICAgbWluVmFsdWUgPC0gb3B0aW0oYygwLjUsIDEgLyAobGVmdCArIDEpKSwgZnVuY3Rpb24oeCkgbV9sb2dfbGlrX2Jpbm9tbG9nKHNhbSwgcCA9IHhbMl0sIG4gPSBsZWZ0LCBxID0geFsxXSkpCiAgICAgICMgcSA8LSBtaW5WYWx1ZSRtaW5pbXVtCiAgICAgIHEgPC0gbWluVmFsdWUkcGFyWzFdCiAgICAgIHAgPC0gbWluVmFsdWUkcGFyWzJdCiAgICAgIG4gPC0gbGVmdAogICAgICBmb3IgKGkgaW4gKGxlZnQgKyAxKTpyaWdodCl7CiAgICAgICAgIyB2YWx1ZSA8LSBvcHRpbWl6ZShmdW5jdGlvbih4KSBtX2xvZ19saWtfYmlub21sb2coc2FtLCBwID0gKHZhcl9zYW0gLyBtZWFuX3NhbSAqIGxvZygxIC0geCkgKiAoMSAtIHgpIC0gbG9nKDEgLSB4KSkgLyB4LCBuID0gaSwgcSA9IHgpLCBjKDAuMDEsIDAuOTkpKQogICAgICAgIHZhbHVlIDwtIG9wdGltKGMoMC41LCAxIC8gaSksIGZ1bmN0aW9uKHgpIG1fbG9nX2xpa19iaW5vbWxvZyhzYW0sIHAgPSB4WzJdLCBuID0gaSwgcSA9IHhbMV0pKQogICAgICAgICMgaWYgKHZhbHVlJG9iamVjdGl2ZSA8PSBtaW5WYWx1ZSRvYmplY3RpdmUpewogICAgICAgICMgICBtaW5WYWx1ZSA8LSB2YWx1ZQogICAgICAgICMgICBxIDwtIHZhbHVlJG1pbmltdW0KICAgICAgICAjICAgbiA8LSBpCiAgICAgICAgIyB9CiAgICAgICAgaWYgKHZhbHVlJHZhbHVlIDw9IG1pblZhbHVlJHZhbHVlKXsKICAgICAgICAgIG1pblZhbHVlIDwtIHZhbHVlCiAgICAgICAgICBxIDwtIG1pblZhbHVlJHBhclsxXQogICAgICAgICAgcCA8LSBtaW5WYWx1ZSRwYXJbMl0KICAgICAgICAgIG4gPC0gaQogICAgICAgIH0KICAgICAgfQogICAgfQogICAgZWxzZSB7CiAgICAgIHEgPC0gb3B0aW1pemUoZnVuY3Rpb24oeCkgbV9sb2dfbGlrX2Jpbm9tbG9nKHNhbSwgcCA9ICh2YXJfc2FtIC8gbWVhbl9zYW0gKiBsb2coMSAtIHgpICogKDEgLSB4KSAtIGxvZygxIC0geCkpIC8geCwgbiA9IG4sIHEgPSB4KSwgYygwLjAxLCAwLjk5KSkkbWluaW11bQogICAgICBwIDwtICh2YXJfc2FtIC8gbWVhbl9zYW0gKiBsb2coMSAtIHEpICogKDEgLSBxKSAtIGxvZygxIC0gcSkpIC8gcQogICAgfQoKICAgICMgcmVzIDwtIGMobiwgcSwgKHZhcl9zYW0gLyBtZWFuX3NhbSAqIGxvZygxIC0gcSkgKiAoMSAtIHEpIC0gbG9nKDEgLSBxKSkgLyBxKQogICAgcmVzIDwtIGMobiwgcSwgcCkKICB9CiAgZWxzZSBpZiAoZGlzdHIgPT0gJ2xvZ2Jpbm9tJyl7CiAgICBpZiAobiA9PSAwKXsKICAgICAgbGVmdCA8LSAxCiAgICAgIHJpZ2h0IDwtIDEwMDAKICAgICAgd2hpbGUgKHJpZ2h0IC0gbGVmdCA+IDIpIHsKICAgICAgICBuTGVmdCA8LSAoMiAqIGxlZnQgKyByaWdodCkgJS8lIDMKICAgICAgICBuUmlnaHQgPC0gKGxlZnQgKyAyICogcmlnaHQpICUvJSAzCiAgICAgICAgIyBxTGVmdCA8LSBvcHRpbWl6ZShmdW5jdGlvbih4KSBtX2xvZ19saWtfbG9nYmlub20oc2FtLCBxID0geCwgcCA9IC0gbG9nKDEgLSB4KSAqICgxIC0geCkgLyBuTGVmdCAvIHggKiBtZWFuX3NhbSwgbiA9IG5MZWZ0KSwgYygwLjAxLCAwLjk5KSkKICAgICAgICAjIHFSaWdodCA8LSBvcHRpbWl6ZShmdW5jdGlvbih4KSBtX2xvZ19saWtfbG9nYmlub20oc2FtLCBxID0geCwgcCA9IC0gbG9nKDEgLSB4KSAqICgxIC0geCkgLyBuUmlnaHQgLyB4ICogbWVhbl9zYW0sIG4gPSBuUmlnaHQpLCBjKDAuMDEsIDAuOTkpKQogICAgICAgIHFMZWZ0IDwtIG9wdGltKGMoMC41LCAxIC8gbkxlZnQpLCBmdW5jdGlvbih4KSBtX2xvZ19saWtfbG9nYmlub20oc2FtLCBxID0geFsxXSwgcCA9IHhbMl0sIG4gPSBuTGVmdCkpCiAgICAgICAgcVJpZ2h0IDwtIG9wdGltKGMoMC41LCAxIC8gblJpZ2h0KSwgZnVuY3Rpb24oeCkgbV9sb2dfbGlrX2xvZ2Jpbm9tKHNhbSwgcSA9IHhbMV0sIHAgPSB4WzJdLCBuID0gblJpZ2h0KSkKICAgICAgICAKICAgICAgICAjIGlmIChxTGVmdCRvYmplY3RpdmUgPD0gcVJpZ2h0JG9iamVjdGl2ZSkgewogICAgICAgICMgICByaWdodCA8LSBuUmlnaHQgLSAxCiAgICAgICAgIyB9CiAgICAgICAgIyBlbHNlewogICAgICAgICMgICBsZWZ0IDwtIG5MZWZ0ICsgMQogICAgICAgICMgfQogICAgICAgIGlmIChxTGVmdCR2YWx1ZSA8PSBxUmlnaHQkdmFsdWUpIHsKICAgICAgICAgIHJpZ2h0IDwtIG5SaWdodAogICAgICAgIH0KICAgICAgICBlbHNlewogICAgICAgICAgbGVmdCA8LSBuTGVmdAogICAgICAgIH0KICAgICAgfQogICAgICAKICAgICAgIyBtaW5WYWx1ZSA8LSBvcHRpbWl6ZShmdW5jdGlvbih4KSBtX2xvZ19saWtfbG9nYmlub20oc2FtLCBxID0geCwgcCA9IC0gbG9nKDEgLSB4KSAqICgxIC0geCkgLyBsZWZ0IC8geCAqIG1lYW5fc2FtLCBuID0gbGVmdCksIGMoMC4wMSwgMC45OSkpCiAgICAgIG1pblZhbHVlIDwtIG9wdGltKGMoMC41LCAxIC8gKGxlZnQgKyAxKSksIGZ1bmN0aW9uKHgpIG1fbG9nX2xpa19sb2diaW5vbShzYW0sIHEgPSB4WzFdLCBwID0geFsyXSwgbiA9IGxlZnQpKQogICAgICAjIHEgPC0gbWluVmFsdWUkbWluaW11bQogICAgICBxIDwtIG1pblZhbHVlJHBhclsxXQogICAgICBwIDwtIG1pblZhbHVlJHBhclsyXQogICAgICBuIDwtIGxlZnQKICAgICAgZm9yIChpIGluIChsZWZ0ICsgMSk6cmlnaHQpewogICAgICAgICMgdmFsdWUgPC0gb3B0aW1pemUoZnVuY3Rpb24oeCkgbV9sb2dfbGlrX2xvZ2Jpbm9tKHNhbSwgcSA9IHgsIHAgPSAtIGxvZygxIC0geCkgKiAoMSAtIHgpIC8gaSAvIHggKiBtZWFuX3NhbSwgbiA9IGkpLCBjKDAuMDEsIDAuOTkpKQogICAgICAgIHZhbHVlIDwtIG9wdGltKGMoMC41LCAxIC8gaSksIGZ1bmN0aW9uKHgpIG1fbG9nX2xpa19sb2diaW5vbShzYW0sIHEgPSB4WzFdLCBwID0geFsyXSwgbiA9IGkpKQogICAgICAgICMgaWYgKHZhbHVlJG9iamVjdGl2ZSA8PSBtaW5WYWx1ZSRvYmplY3RpdmUpewogICAgICAgICMgICBtaW5WYWx1ZSA8LSB2YWx1ZQogICAgICAgICMgICBxIDwtIHZhbHVlJG1pbmltdW0KICAgICAgICAjICAgbiA8LSBpCiAgICAgICAgIyB9CiAgICAgICAgaWYgKHZhbHVlJHZhbHVlIDw9IG1pblZhbHVlJHZhbHVlKXsKICAgICAgICAgIG1pblZhbHVlIDwtIHZhbHVlCiAgICAgICAgICBxIDwtIG1pblZhbHVlJHBhclsxXQogICAgICAgICAgcCA8LSBtaW5WYWx1ZSRwYXJbMl0KICAgICAgICAgIG4gPC0gaQogICAgICAgIH0KICAgICAgfQogICAgfQogICAgZWxzZSB7CiAgICAgIHEgPC0gb3B0aW1pemUoZnVuY3Rpb24oeCkgbV9sb2dfbGlrX2xvZ2Jpbm9tKHNhbSwgcSA9IHgsIHAgPSAtIGxvZygxIC0geCkgKiAoMSAtIHgpIC8gbiAvIHggKiBtZWFuX3NhbSwgbiA9IG4pLCBjKDAuMDEsIDAuOTkpKSRtaW5pbXVtCiAgICAgIHAgPC0gLWxvZygxIC0gcSkgKiAoMSAtIHEpIC8gbiAvIHEgKiBtZWFuX3NhbQogICAgfQogICAgCiAgICAjIHJlcyA8LSBjKG4sIHEsIC0gbG9nKDEgLSBxKSAqICgxIC0gcSkgLyBuIC8gcSAqIG1lYW5fc2FtKQogICAgcmVzIDwtIGMobiwgcSwgcCkKICB9IGVsc2UgaWYgKGRpc3RyID09ICdsb2dwb2lzJyl7CiAgICBxIDwtIG9wdGltKGMoMC41LCAxKSwgZnVuY3Rpb24oeCkgbV9sb2dfbGlrX2xvZ3BvaXMoc2FtLCBxID0geFsxXSwgbGFtYmRhID0geFsyXSkpCiAgICBsYW1iZGEgPC0gcSRwYXJbMl0KICAgIHEgPC0gcSRwYXJbMV0KICAgIHJlcyA8LSBjKGxhbWJkYSwgcSkKICB9IGVsc2UgaWYgKGRpc3RyID09ICduYmlub20nKXsKICAgIHEgPC0gb3B0aW0oYygwLjUsIDEpLCBmdW5jdGlvbih4KSBtX2xvZ19saWtfbmJpbm9tKHNhbSwgcSA9IHhbMV0sIG4gPSB4WzJdKSkKICAgIG4gPC0gcSRwYXJbMl0KICAgIHEgPC0gcSRwYXJbMV0KICAgIHJlcyA8LSBjKG4sIHEpCiAgfSBlbHNlIGlmIChkaXN0ciA9PSAiZG91YmxlbmJpbm9tIil7CiAgICBxIDwtIG9wdGltKGMoMC44LCAxLCAwLjUsIDEpLAogICAgICAgICAgICAgICBmdW5jdGlvbih4KSBtX2xvZ19saWtfZG91YmxlbmJpbm9tKHNhbSwgcF8xID0geFsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplXzEgPSB4WzJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBfMiA9IHhbM10sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZV8yID0geFs0XSkpCiAgICByZXMgPC0gYyhxJHBhclsxXSwgcSRwYXJbMl0sIHEkcGFyWzNdLCBxJHBhcls0XSkKICB9IGVsc2UgaWYgKGRpc3RyID09ICJ5dWxlc2ltb25uYmlub20iKXsKICAgIHEgPC0gb3B0aW0oYygwLjEsIDEpLAogICAgICAgICAgICAgICBmdW5jdGlvbih4KSBtX2xvZ19saWtfeXVsZV9zaW1vbl9uYmlub20oc2FtLCBxID0geFsxXSwgbiA9IHhbMl0pKQogICAgcmVzIDwtIGMocSRwYXJbMV0sIHEkcGFyWzJdKQogIH0gZWxzZSBpZiAoZGlzdHIgPT0gInBvaXMiKXsKICAgIGxhbWJkYSA8LSBvcHRpbWl6ZShmdW5jdGlvbih4KSBtX2xvZ19saWtfcG9pcyhzYW0sIGxhbWJkYSA9IHgpLCBjKDAsIDEwMDApKSRtaW5pbXVtCiAgICByZXMgPC0gYyhsYW1iZGEpCiAgfQogIAogICMgcHJpbnQocmVzKQoKICBwcm9iIDwtIGMoKQogIAogIGZvciAoaSBpbiAwOihsZW5ndGgoZXhwX3Byb2IpIC0gMikpewogICAgaWYgKGRpc3RyID09ICdiaW5vbWxvZycpewogICAgICBwcm9iIDwtIGMocHJvYiwgZ2V0X3Byb2JfYmlub21sb2coaSwgcCA9IHJlc1szXSwgbiA9IHJlc1sxXSwgcSA9IHJlc1syXSkpCiAgICB9IGVsc2UgaWYgKGRpc3RyID09ICdsb2diaW5vbScpewogICAgICBwcm9iIDwtIGMocHJvYiwgZ2V0X3Byb2JfbG9nYmlub20oaSwgcSA9IHJlc1syXSwgcCA9IHJlc1szXSwgbiA9IHJlc1sxXSkpCiAgICB9IGVsc2UgaWYgKGRpc3RyID09ICdsb2dwb2lzJyl7CiAgICAgIHByb2IgPC0gYyhwcm9iLCBnZXRfcHJvYl9sb2dwb2lzKGksIHEgPSByZXNbMl0sIGxhbWJkYSA9IHJlc1sxXSkpCiAgICB9IGVsc2UgaWYgKGRpc3RyID09ICduYmlub20nKXsKICAgICAgcHJvYiA8LSBjKHByb2IsIGRuYmlub20oaSwgc2l6ZSA9IHJlc1sxXSwgcHJvYiA9IHJlc1syXSkpCiAgICB9IGVsc2UgaWYgKGRpc3RyID09ICdkb3VibGVuYmlub20nKXsKICAgICAgcHJvYiA8LSBjKHByb2IsIGdldF9wcm9iX2RvdWJsZW5iaW5vbShpLCBwXzEgPSByZXNbMV0sIHNpemVfMSA9IHJlc1syXSwgcF8yID0gcmVzWzNdLCBzaXplXzIgPSByZXNbNF0pKQogICAgfSBlbHNlIGlmIChkaXN0ciA9PSAieXVsZXNpbW9ubmJpbm9tIil7CiAgICAgIHByb2IgPC0gYyhwcm9iLCBnZXRfcHJvYl95dWxlX3NpbW9uX25iaW5vbShpLCBxID0gcmVzWzFdLCBuID0gcmVzWzJdKSkKICAgIH0gZWxzZSBpZiAoZGlzdHIgPT0gInBvaXMiKXsKICAgICAgcHJvYiA8LSBjKHByb2IsIGRwb2lzKGksIGxhbWJkYSA9IHJlc1sxXSkpCiAgICB9IAogIH0KICAKICBwcm9iIDwtIGMocHJvYiwgMSAtIHN1bShwcm9iKSkKICAKICBtYXhfZWxfeCA9IG1heChzYW0pCiAgbWF4X2VsX3kgPSBtYXgoZXhwX3Byb2IsIHByb2IpCiAgCiAgaWYgKGdldF9oaXN0KXsKICAgIGhpc3QuZGVmYXVsdChzYW0sIHByb2JhYmlsaXR5ID0gVFJVRSwgYnJlYWtzID0gc2VxKC0wLjUsIG1heF9lbF94ICsgMC41LCAxKSwgeGxpbSA9IGMoLTAuNSwgbWF4X2VsX3ggKyAwLjUpLCB5bGltID0gYygwLCBtYXhfZWxfeSksIHhsYWIgPSAi0JLRi9Cx0L7RgNC60LAiLCB5bGFiID0gItCS0LXRgNC+0Y/RgtC90L7RgdGC0YwiLCBtYWluID0gIiIpCiAgICBwb2ludHMoMDoobGVuZ3RoKHByb2IpIC0gMSksIHByb2IpCiAgfQogIAogIGZvciAoaSBpbiAxOmxlbmd0aChwcm9iKSl7CiAgICB3aGlsZSAoMTAwICogcHJvYltpXSA8IDUgJiYgaSA8IGxlbmd0aChwcm9iKSl7CiAgICAgIHByb2JbaV0gPC0gcHJvYltpXSArIHByb2JbaSArIDFdCiAgICAgIHByb2IgPC0gcHJvYlstKGkgKyAxKV0KICAgICAgZXhwX3Byb2JbaV0gPC0gZXhwX3Byb2JbaV0gKyBleHBfcHJvYltpICsgMV0KICAgICAgZXhwX3Byb2IgPC0gZXhwX3Byb2JbLShpICsgMSldCiAgICB9CiAgfQogIAogIGlmICgxMDAgKiBwcm9iW2xlbmd0aChwcm9iKV0gPCA1KXsKICAgIHByb2JbbGVuZ3RoKHByb2IpIC0gMV0gPC0gcHJvYltsZW5ndGgocHJvYikgLSAxXSArIHByb2JbbGVuZ3RoKHByb2IpXQogICAgcHJvYiA8LSBwcm9iWy1sZW5ndGgocHJvYildCiAgICBleHBfcHJvYltsZW5ndGgoZXhwX3Byb2IpIC0gMV0gPC0gZXhwX3Byb2JbbGVuZ3RoKGV4cF9wcm9iKSAtIDFdICsgZXhwX3Byb2JbbGVuZ3RoKGV4cF9wcm9iKV0KICAgIGV4cF9wcm9iIDwtIGV4cF9wcm9iWy1sZW5ndGgoZXhwX3Byb2IpXQogIH0KICAKICBkZiA8LSBkZiArIGxlbmd0aChwcm9iKSAtIDMKCiAgaWYgKGRmIDwgMSl7CiAgICBkZiA8LSAxCiAgfQogIAogIGNoaSA8LSBOICogbXlfY2hpc3EoZXhwX3Byb2IsIHByb2IpCiAgcmVzIDwtIGMocmVzLCAxIC0gcGNoaXNxKGNoaSwgZGYpKQogIAogIHJlcwp9CmBgYAoKIyMjINCg0LDQtNC40L7QsdC40L7Qu9C+0LPQuNGH0LXRgdC60LjQtSDQtNCw0L3QvdGL0LUKCtCk0YPQvdC60YbQuNGPINC/0YDQsNCy0LTQvtC/0L7QtNC+0LHQuNGPINC00LvRjyDQm9CR0KAg0Lgg0JHQm9CgOgoKYGBge3J9CmZ1bmNNUF8zRCA8LSBmdW5jdGlvbihkYXRhLCBkaXN0ciA9ICdiaW5vbWxvZycsIGludGVyX3BhcjEgPSBjKDAuMDEsIDAuOTksIDEwMCksIGludGVyX3BhcjIgPSBjKDEsIDEwMCwgMTApKXsKICBsaWJyYXJ5KHJnbCkKICAKICBvcGVuM2QoKQogIGxpbmVzM2QoYygwLCAwKSwgYygwLCAwKSwgYygwLCAxMiksIGNvbG9yID0gImdyYXkiKTsKICBsaW5lczNkKGMoMCwgMTIpLCBjKDAsIDApLCBjKDAsIDApLCBjb2xvciA9ICJncmF5Iik7CiAgbGluZXMzZChjKDAsIDApLCBjKDAsIDEyKSwgYygwLCAwKSwgY29sb3IgPSAiZ3JheSIpOwogIAogIHNhbSA8LSBnZW5lcmF0ZV9zYW1wbGUoZGF0YSkKICBOIDwtIGxlbmd0aChzYW0pCiAgdmFyX3NhbSA9IHZhcihzYW0pICogKE4gLSAxKSAvIE4KICBtZWFuX3NhbSA9IG1lYW4oc2FtKQogIHggPC0gYygpCiAgeSA8LSBjKCkKICB6IDwtIGMoKQogIGZvciAocGFyMSBpbiBzZXEoaW50ZXJfcGFyMVsxXSwgaW50ZXJfcGFyMVsyXSwgKGludGVyX3BhcjFbMl0gLSBpbnRlcl9wYXIxWzFdKSAvIGludGVyX3BhcjFbM10pKXsKICAgIGZvciAocGFyMiBpbiBzZXEoaW50ZXJfcGFyMlsxXSwgaW50ZXJfcGFyMlsyXSwgKGludGVyX3BhcjJbMl0gLSBpbnRlcl9wYXIyWzFdKSAvIGludGVyX3BhcjJbM10pKXsKICAgICAgaWYgKGRpc3RyID09ICdiaW5vbWxvZycpewogICAgICAgIHZhbHVlIDwtIG1fbG9nX2xpa19iaW5vbWxvZyhzYW0sIHAgPSAodmFyX3NhbSAvIG1lYW5fc2FtICogbG9nKDEgLSBwYXIxKSAqICgxIC0gcGFyMSkgLSBsb2coMSAtIHBhcjEpKSAvIHBhcjEsIG4gPSBwYXIyLCBxID0gcGFyMSkKICAgICAgfSBlbHNlIGlmIChkaXN0ciA9PSAnbG9nYmlub20nKXsKICAgICAgICB2YWx1ZSA8LSBtX2xvZ19saWtfbG9nYmlub20oc2FtLCBxID0gcGFyMSwgcCA9IC0gbG9nKDEgLSBwYXIxKSAqICgxIC0gcGFyMSkgLyBwYXIyIC8gcGFyMSAqIG1lYW5fc2FtLCBuID0gcGFyMikKICAgICAgfSBlbHNlIGlmIChkaXN0ciA9PSAnbG9ncG9pcycpewogICAgICAgIHZhbHVlIDwtIG1fbG9nX2xpa19sb2dwb2lzKHNhbSwgcSA9IHBhcjEsIGxhbWJkYSA9IHBhcjIpCiAgICAgIH0KICAgICAgCiAgICAgIGlmICh2YWx1ZSAhPSAwKSB7CiAgICAgICAgeCA8LSBjKHgsIHBhcjEpCiAgICAgICAgeSA8LSBjKHksIHBhcjIpCiAgICAgICAgeiA8LSBjKHosIHZhbHVlKQogICAgICB9CiAgICB9CiAgfQogIAogIHogPC0geiAtIG1pbih6KQogIAogIGlmIChkaXN0ciA9PSAnYmlub21sb2cnKXsKICAgIHogPC0gc3FydCh6KSAvIDEwCiAgICBwb2ludHMzZCgxMCAqIHhbeiA8IDEyXSwgeVt6IDwgMTJdICogMTAgLyBpbnRlcl9wYXIyWzJdLCB6W3ogPCAxMl0sIGNvbG9yID0iYmxhY2siKQogIH0gZWxzZSBpZiAoZGlzdHIgPT0gJ2xvZ2Jpbm9tJyl7CiAgICBwb2ludHMzZCgxMCAqIHhbeiA8IDEyXSwgeVt6IDwgMTJdICogMTAgLyBpbnRlcl9wYXIyWzJdLCB6W3ogPCAxMl0sIGNvbG9yID0iYmxhY2siKQogIH0gZWxzZSBpZiAoZGlzdHIgPT0gJ2xvZ3BvaXMnKXsKICAgIHBvaW50czNkKDEwICogeFt6IDwgMTJdLCB5W3ogPCAxMl0gKiAxMCAvIGludGVyX3BhcjJbMl0sIHpbeiA8IDEyXSwgY29sb3IgPSJibGFjayIpCiAgfQp9CmBgYAoK0J/RgNC+0LLQtdGA0LrQsCDQvtGG0LXQvdC60Lgg0LTQu9GPINCb0JHQoDoKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CnNhbUxCUiA8LSBybG9nYmlub20oMTAwMCwgbG9nX3ByZXBlcmluZygwLjQpLCA2LCAwLjMpCnNhbUxCUiA8LSBzYXBwbHkoc2FtTEJSLCBmdW5jdGlvbih4KSBpZiAoeCA+IDQpezV9IGVsc2Uge3h9KQpoaXN0TEJSIDwtIGhpc3QoYXMubnVtZXJpYyhzYW1MQlIpLCBwcm9iYWJpbGl0eSA9IFRSVUUsIGJyZWFrcyA9IHNlcSgtMC41LCBtYXgoc2FtTEJSKSArIDAuNSwgMSksIHhsaW0gPSBjKC0wLjUsIG1heChzYW1MQlIpICsgMC41KSwgcGxvdCA9IFRSVUUpCnJlcyA8LSBoaXN0X21ha2UoMCwgYXMubnVtZXJpYyhoaXN0TEJSJGNvdW50cyksIGdldF9oaXN0ID0gVFJVRSwgJ2xvZ2Jpbm9tJykKIyBmdW5jTVBfM0QoaGlzdExCUiRjb3VudHMsIGRpc3RyID0gJ2xvZ2Jpbm9tJykKcmVzCmBgYAoKYGBge3J9CnFfTEJSIDwtIGMoKQpuX0xCUiA8LSBjKCkKcF9MQlIgPC0gYygpCnNlcV9MQlIgPC0gc2VxKDEwMCwgMTAwMDAsIDUwMCkKc2FtTEJSIDwtIHJsb2diaW5vbSgxMDAwMCwgbG9nX3ByZXBlcmluZygwLjQpLCA2LCAwLjMpCnNhbUxCUiA8LSBzYXBwbHkoc2FtTEJSLCBmdW5jdGlvbih4KSBpZiAoeCA+IDQpezV9IGVsc2Uge3h9KQoKZm9yIChpIGluIHNlcV9MQlIpewogIGhpc3RMQlIgPC0gaGlzdChhcy5udW1lcmljKHNhbUxCUlsxOmldKSwgYnJlYWtzID0gc2VxKC0wLjUsIG1heChzYW1MQlIpICsgMC41LCAxKSwgcGxvdCA9IEZBTFNFKQogIHJlcyA8LSBoaXN0X21ha2UoMCwgYXMubnVtZXJpYyhoaXN0TEJSJGNvdW50cyksIGdldF9oaXN0ID0gRkFMU0UsICdsb2diaW5vbScpCiAgbl9MQlIgPC0gYyhuX0xCUiwgcmVzWzFdKQogIHFfTEJSIDwtIGMocV9MQlIsIHJlc1syXSkKICBwX0xCUiA8LSBjKHBfTEJSLCByZXNbM10pCn0KYGBgCgpgYGB7cn0KcGFyKG1mcm93ID0gYygxLCAzKSkKcGxvdCh4ID0gc2VxX0xCUiwgeSA9IHFfTEJSLCB4bGFiID0gItCg0LDQt9C80LXRgCDQstGL0LHQvtGA0LrQuCIsIHlsYWIgPSAi0J7RhtC10L3QutCwINC/0LDRgNCw0LzQtdGC0YDQsCBxINC70L7Qs9Cw0YDQuNGE0LzQsCIpCmFibGluZShoID0gMC40LCBsdHkgPSAiZGFzaGVkIikKbGVnZW5kKHggPSAidG9wcmlnaHQiLCBsZWdlbmQgPSAi0JjRgdGC0LjQvdC90L7QtSDQt9C90LDRh9C10L3QuNC1IiwgbHR5ID0gMiwgY29sID0gMSwgbHdkID0gMikKcGxvdCh4ID0gc2VxX0xCUiwgeSA9IG5fTEJSLCB4bGFiID0gItCg0LDQt9C80LXRgCDQstGL0LHQvtGA0LrQuCIsIHlsYWIgPSAi0J7RhtC10L3QutCwINC/0LDRgNCw0LzQtdGC0YDQsCBuINCx0LjQvdC+0LzQsCIpCmFibGluZShoID0gNiwgbHR5ID0gImRhc2hlZCIpCmxlZ2VuZCh4ID0gInRvcHJpZ2h0IiwgbGVnZW5kID0gItCY0YHRgtC40L3QvdC+0LUg0LfQvdCw0YfQtdC90LjQtSIsIGx0eSA9IDIsIGNvbCA9IDEsIGx3ZCA9IDIpCnBsb3QoeCA9IHNlcV9MQlIsIHkgPSBwX0xCUiwgeGxhYiA9ICLQoNCw0LfQvNC10YAg0LLRi9Cx0L7RgNC60LgiLCB5bGFiID0gItCe0YbQtdC90LrQsCDQv9Cw0YDQsNC80LXRgtGA0LAgcCDQsdC40L3QvtC80LAiKQphYmxpbmUoaCA9IDAuMywgbHR5ID0gImRhc2hlZCIpCmxlZ2VuZCh4ID0gImJvdHRvbXJpZ2h0IiwgbGVnZW5kID0gItCY0YHRgtC40L3QvdC+0LUg0LfQvdCw0YfQtdC90LjQtSIsIGx0eSA9IDIsIGNvbCA9IDEsIGx3ZCA9IDIpCnBhcihtZnJvdyA9IGMoMSwgMSkpCmBgYAoK0J/RgNC+0LLQtdGA0LrQsCDQvtGG0LXQvdC60Lgg0LTQu9GPINCR0JvQoDoKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CnNhbUJMUiA8LSByYmlub21sb2coMTAwMCwgOCwgMC4yNSwgbG9nX3ByZXBlcmluZygwLjIpKQpoaXN0QkxSIDwtIGhpc3QoYXMubnVtZXJpYyhzYW1CTFIpLCBwcm9iYWJpbGl0eSA9IFRSVUUsIGJyZWFrcyA9IHNlcSgtMC41LCBtYXgoc2FtQkxSKSArIDAuNSwgMSksIHhsaW0gPSBjKC0wLjUsIG1heChzYW1CTFIpICsgMC41KSwgcGxvdCA9IFRSVUUsIHhsYWIgPSAi0JLRi9Cx0L7RgNC60LAiLCB5bGFiID0gItCS0LXRgNC+0Y/RgtC90L7RgdGC0YwiLCBtYWluID0gIiIpCnJlcyA8LSBoaXN0X21ha2UoMCwgYXMubnVtZXJpYyhoaXN0QkxSJGNvdW50cyksIGdldF9oaXN0ID0gVFJVRSwgJ2Jpbm9tbG9nJykKZnVuY01QXzNEKGhpc3RCTFIkY291bnRzLCBpbnRlcl9wYXIyID0gYygxLCAxMDAsIDEwMCkpCnJlcwpgYGAKCmBgYHtyfQpxX0JMUiA8LSBjKCkKbl9CTFIgPC0gYygpCnBfQkxSIDwtIGMoKQpzZXFfQkxSIDwtIHNlcSgxMDAsIDEwMDAwLCA1MDApCnNhbUJMUiA8LSByYmlub21sb2coMTAwMDAsIDYsIDAuMywgbG9nX3ByZXBlcmluZygwLjQpKQoKZm9yIChpIGluIHNlcV9CTFIpewogIGhpc3RCTFIgPC0gaGlzdChhcy5udW1lcmljKHNhbUJMUlsxOmldKSwgYnJlYWtzID0gc2VxKC0wLjUsIG1heChzYW1CTFIpICsgMC41LCAxKSwgcGxvdCA9IEZBTFNFKQogIHJlcyA8LSBoaXN0X21ha2UoMCwgYXMubnVtZXJpYyhoaXN0QkxSJGNvdW50cyksIGdldF9oaXN0ID0gRkFMU0UsICdiaW5vbWxvZycpCiAgbl9CTFIgPC0gYyhuX0JMUiwgcmVzWzFdKQogIHFfQkxSIDwtIGMocV9CTFIsIHJlc1syXSkKICBwX0JMUiA8LSBjKHBfQkxSLCByZXNbM10pCiAgcHJpbnQoaSkKfQpgYGAKCmBgYHtyfQpwYXIobWZyb3cgPSBjKDEsIDMpKQpwbG90KHggPSBzZXFfQkxSLCB5ID0gcV9CTFIsIHhsYWIgPSAi0KDQsNC30LzQtdGAINCy0YvQsdC+0YDQutC4IiwgeWxhYiA9ICLQntGG0LXQvdC60LAg0L/QsNGA0LDQvNC10YLRgNCwIHEg0LvQvtCz0LDRgNC40YTQvNCwIikKYWJsaW5lKGggPSAwLjQsIGx0eSA9ICJkYXNoZWQiKQpsZWdlbmQoeCA9ICJib3R0b21yaWdodCIsIGxlZ2VuZCA9ICLQmNGB0YLQuNC90L3QvtC1INC30L3QsNGH0LXQvdC40LUiLCBsdHkgPSAyLCBjb2wgPSAxLCBsd2QgPSAyKQpwbG90KHggPSBzZXFfQkxSLCB5ID0gbl9CTFIsIHhsYWIgPSAi0KDQsNC30LzQtdGAINCy0YvQsdC+0YDQutC4IiwgeWxhYiA9ICLQntGG0LXQvdC60LAg0L/QsNGA0LDQvNC10YLRgNCwIG4g0LHQuNC90L7QvNCwIikKYWJsaW5lKGggPSA2LCBsdHkgPSAiZGFzaGVkIikKbGVnZW5kKHggPSAidG9wcmlnaHQiLCBsZWdlbmQgPSAi0JjRgdGC0LjQvdC90L7QtSDQt9C90LDRh9C10L3QuNC1IiwgbHR5ID0gMiwgY29sID0gMSwgbHdkID0gMikKcGxvdCh4ID0gc2VxX0JMUiwgeSA9IHBfQkxSLCB4bGFiID0gItCg0LDQt9C80LXRgCDQstGL0LHQvtGA0LrQuCIsIHlsYWIgPSAi0J7RhtC10L3QutCwINC/0LDRgNCw0LzQtdGC0YDQsCBwINCx0LjQvdC+0LzQsCIpCmFibGluZShoID0gMC4zLCBsdHkgPSAiZGFzaGVkIikKbGVnZW5kKHggPSAiYm90dG9tcmlnaHQiLCBsZWdlbmQgPSAi0JjRgdGC0LjQvdC90L7QtSDQt9C90LDRh9C10L3QuNC1IiwgbHR5ID0gMiwgY29sID0gMSwgbHdkID0gMikKcGFyKG1mcm93ID0gYygxLCAxKSkKYGBgCgrQn9GA0L7QstC10YDQutCwINC+0YbQtdC90LrQuCDQtNC70Y8g0JvQn9CgOgoKYGBge3Igd2FybmluZz1GQUxTRX0Kc2FtTFBSIDwtIHJsb2dwb2lzKDEwMDAsIGxvZ19wcmVwZXJpbmcoMC4yNSksIDMuNDUpCmhpc3RMUFIgPC0gaGlzdChhcy5udW1lcmljKHNhbUxQUlsxOjEwMF0pLCBwcm9iYWJpbGl0eSA9IFRSVUUsIGJyZWFrcyA9IHNlcSgtMC41LCBtYXgoc2FtTFBSKSArIDAuNSwgMSksIHhsaW0gPSBjKC0wLjUsIG1heChzYW1MUFIpICsgMC41KSwgcGxvdCA9IFRSVUUpCnJlcyA8LSBoaXN0X21ha2UoMCwgYXMubnVtZXJpYyhoaXN0TFBSJGNvdW50cyksIGdldF9oaXN0ID0gVFJVRSwgJ2xvZ3BvaXMnKQojIGZ1bmNNUF8zRChoaXN0TFBSJGNvdW50cywgZGlzdHIgPSAnbG9ncG9pcycsIGludGVyX3BhcjEgPSBjKDAuMDEsIDAuOTksIDEwMCksIGludGVyX3BhcjIgPSBjKDMuMSwgNC4xLCAxMCkpCnJlcwpgYGAKCtCh0L7RgdGC0L7Rj9GC0LXQu9GM0L3QvtGB0YLRjDoKCmBgYHtyfQpxX0xQUiA8LSBjKCkKbGFtYmRhX0xQUiA8LSBjKCkKc2FtTFBSIDwtIHJsb2dwb2lzKDEwMDAwLCBsb2dfcHJlcGVyaW5nKDAuMjUpLCAzLjQ1KQoKZm9yIChpIGluIHNlcSgxMDAsIDEwMDAwLCAxMDApKXsKICBoaXN0TFBSIDwtIGhpc3QoYXMubnVtZXJpYyhzYW1MUFJbMTppXSksIGJyZWFrcyA9IHNlcSgtMC41LCBtYXgoc2FtTFBSKSArIDAuNSwgMSksIHBsb3QgPSBGQUxTRSkKICByZXMgPC0gaGlzdF9tYWtlKDAsIGFzLm51bWVyaWMoaGlzdExQUiRjb3VudHMpLCBnZXRfaGlzdCA9IEZBTFNFLCAnbG9ncG9pcycpCiAgbGFtYmRhX0xQUiA8LSBjKGxhbWJkYV9MUFIsIHJlc1sxXSkKICBxX0xQUiA8LSBjKHFfTFBSLCByZXNbMl0pCn0KYGBgCgpgYGB7cn0KcGxvdCh4ID0gc2VxKDEwMCwgMTAwMDAsIDEwMCksIHkgPSBxX0xQUiwgeGxhYiA9ICLQoNCw0LfQvNC10YAg0LLRi9Cx0L7RgNC60LgiLCB5bGFiID0gItCe0YbQtdC90LrQsCDQv9Cw0YDQsNC80LXRgtGA0LAg0LvQvtCz0LDRgNC40YTQvNCwIikKYWJsaW5lKGggPSAwLjI1LCBsdHkgPSAiZGFzaGVkIikKbGVnZW5kKHggPSAiYm90dG9tcmlnaHQiLCBsZWdlbmQgPSAi0JjRgdGC0LjQvdC90L7QtSDQt9C90LDRh9C10L3QuNC1IiwgbHR5ID0gMiwgY29sID0gMSwgbHdkID0gMikKcGxvdCh4ID0gc2VxKDEwMCwgMTAwMDAsIDEwMCksIHkgPSBsYW1iZGFfTFBSLCB4bGFiID0gItCg0LDQt9C80LXRgCDQstGL0LHQvtGA0LrQuCIsIHlsYWIgPSAi0J7RhtC10L3QutCwINC/0LDRgNCw0LzQtdGC0YDQsCDQn9GD0LDRgdGB0L7QvdCwIikKYWJsaW5lKGggPSAzLjQ1LCBsdHkgPSAiZGFzaGVkIikKbGVnZW5kKHggPSAiYm90dG9tcmlnaHQiLCBsZWdlbmQgPSAi0JjRgdGC0LjQvdC90L7QtSDQt9C90LDRh9C10L3QuNC1IiwgbHR5ID0gMiwgY29sID0gMSwgbHdkID0gMikKYGBgCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpzYW1OQlIgPC0gcm5iaW5vbSgxMDAsIDMsIDAuNCkKc2FtTkJSIDwtIHNhcHBseShzYW1OQlIsIGZ1bmN0aW9uKHgpIGlmICh4ID4gNCl7NX0gZWxzZSB7eH0pCmhpc3RMUFIgPC0gaGlzdChhcy5udW1lcmljKHNhbU5CUiksIHByb2JhYmlsaXR5ID0gVFJVRSwgYnJlYWtzID0gc2VxKC0wLjUsIG1heChzYW1OQlIpICsgMC41LCAxKSwgeGxpbSA9IGMoLTAuNSwgbWF4KHNhbU5CUikgKyAwLjUpLCBwbG90ID0gVFJVRSkKcmVzIDwtIGhpc3RfbWFrZSgwLCBhcy5udW1lcmljKGhpc3RMUFIkY291bnRzKSwgZ2V0X2hpc3QgPSBUUlVFLCAnbmJpbm9tJykKZnVuY01QXzNEKGhpc3RMUFIkY291bnRzLCBkaXN0ciA9ICcnLCBpbnRlcl9wYXIxID0gYygwLjAxLCAwLjk5LCAxMDApLCBpbnRlcl9wYXIyID0gYygwLjAxLCAyLjk5LCAxMDApKQpyZXMKYGBgCgrQl9Cw0LPRgNGD0LfQutCwINGA0LDQtNC40L7QsdC40L7Qu9C+0LPQuNGH0LXRgdC60LjRhSDQtNCw0L3QvdGL0YU6CgpgYGB7cn0KbSA8LSByZWFkLmNzdigiLi9WaXRyb1Zpdm8uY3N2IikKZGYuQkxSIDwtIGRhdGEuZnJhbWUobiA9IHJlcCgwLCAxOSksIHEgPSByZXAoMCwgMTkpLCBwID0gcmVwKDAsIDE5KSwgcF92YWx1ZSA9IHJlcCgwLCAxOSkpCmRmLkxCUiA8LSBkYXRhLmZyYW1lKG4gPSByZXAoMCwgMTkpLCBxID0gcmVwKDAsIDE5KSwgcCA9IHJlcCgwLCAxOSksIHBfdmFsdWUgPSByZXAoMCwgMTkpKQpkZi5MUFIgPC0gZGF0YS5mcmFtZShsYW1iZGEgPSByZXAoMCwgMTkpLCBxID0gcmVwKDAsIDE5KSwgcF92YWx1ZSA9IHJlcCgwLCAxOSkpCmRmLk5CUiA8LSBkYXRhLmZyYW1lKG4gPSByZXAoMCwgMTkpLCBxID0gcmVwKDAsIDE5KSwgcF92YWx1ZSA9IHJlcCgwLCAxOSkpCmRmLlBSIDwtIGRhdGEuZnJhbWUobGFtYmQgPSByZXAoMCwgMTkpLCBwX3ZhbHVlID0gcmVwKDAsIDE5KSkKYGBgCgrQoNCw0YHRgdC10Y/QvdC40LU6CgpgYGB7cn0KZS5kZiA8LSBkYXRhLmZyYW1lKGUgPSBzYXBwbHkoMToxOSwgZnVuY3Rpb24oaSkgZV9jdWxjKGdlbmVyYXRlX3NhbXBsZShtW2ksIF0pKSkpCmUuZGYKYGBgCgrQndCw0YXQvtC20LTQtdC90LjQtSAkbiwgcSwgcCQg0LTQu9GPINC60L7RgtC+0YDRi9GFIHAtdmFsdWUg0LzQsNC60YHQuNC80LDQu9GM0L3QvjoKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmZvciAoaSBpbiAxOjE5KXsKICBwcmludChpKQogIGRmLkJMUltpLCBdIDwtIGhpc3RfbWFrZSgwLCBhcy5udW1lcmljKG1baSxdKSwgZ2V0X2hpc3QgPSBGQUxTRSwgJ2Jpbm9tbG9nJykKICAjIGRmLkxCUltpLCBdIDwtIGhpc3RfbWFrZSgwLCBhcy5udW1lcmljKG1baSxdKSwgZ2V0X2hpc3QgPSBGQUxTRSwgJ2xvZ2Jpbm9tJykKICAjIGRmLkxQUltpLCBdIDwtIGhpc3RfbWFrZSgwLCBhcy5udW1lcmljKG1baSxdKSwgZ2V0X2hpc3QgPSBGQUxTRSwgJ2xvZ3BvaXMnKQogICMgZGYuTkJSW2ksIF0gPC0gaGlzdF9tYWtlKDAsIGFzLm51bWVyaWMobVtpLF0pLCBnZXRfaGlzdCA9IEZBTFNFLCAnbmJpbm9tJykKICAjIGRmLlBSW2ksIF0gPC0gaGlzdF9tYWtlKDAsIGFzLm51bWVyaWMobVtpLF0pLCBnZXRfaGlzdCA9IEZBTFNFLCAncG9pcycpCn0KYGBgCgrQndCw0LnQtNC10L3QvdGL0LUg0L7RhtC10L3QutC4INC4IHAtdmFsdWU6CgpgYGB7cn0KZGYuQkxSCmBgYAoKYGBge3J9CmRmLkxCUgpgYGAKCmBgYHtyfQpkZi5MUFIgPC0gZGYuTFBSIHw+IG11dGF0ZShtZWFuX2xvZyA9IC1xIC8gbG9nKDEgLSBxKSAvICgxIC0gcSkpCmRmLkxQUgpgYGAKCmBgYHtyfQpkZi5OQlIgPC0gZGYuTkJSIHw+IG11dGF0ZShsYW1iZGEgPSAtbiAvIGxvZygxIC0gcSkpCmRmLk5CUgpgYGAKCmBgYHtyfQpkZi5QUgpgYGAKCgrQk9C40YHRgtC+0LPRgNCw0LzQvNGLINC00LvRjyDRgdGC0YDQvtC60Lgg0LTQsNC90L3Ri9GFOgoKYGBge3Igd2FybmluZz1GQUxTRX0KcmVzIDwtIGhpc3RfbWFrZSgwLCBhcy5udW1lcmljKG1bMTgsXSksIGdldF9oaXN0ID0gVFJVRSwgJ2Jpbm9tbG9nJykKcmVzCnJlcyA8LSBoaXN0X21ha2UoMTAwMCwgYXMubnVtZXJpYyhtWzE4LF0pLCBnZXRfaGlzdCA9IFRSVUUsICdsb2diaW5vbScpCnJlcwpyZXMgPC0gaGlzdF9tYWtlKDEwMDAsIGFzLm51bWVyaWMobVsxMyxdKSwgZ2V0X2hpc3QgPSBUUlVFLCAnbG9ncG9pcycpCnJlcwpgYGAKCtCk0YPQvdC60YbQuNGPINC/0YDQsNCy0LTQvtC/0L7QtNC+0LHQuNGPOgoKYGBge3Igd2FybmluZz1GQUxTRX0KZnVuY01QXzNEKGFzLm51bWVyaWMobVsxLF0pLCBkaXN0ciA9ICdiaW5vbWxvZycsIGludGVyX3BhcjIgPSBjKDEsIDIwLCAyMCkpCmZ1bmNNUF8zRChhcy5udW1lcmljKG1bNyxdKSwgZGlzdHIgPSAnbG9nYmlub20nLCBpbnRlcl9wYXIyID0gYygxLCAyMCwgMTAwKSkKZnVuY01QXzNEKGFzLm51bWVyaWMobVsxMSxdKSwgZGlzdHIgPSAnbG9ncG9pcycsIGludGVyX3BhcjEgPSBjKDAuMDEsIDAuOTksIDEwMCksIGludGVyX3BhcjIgPSBjKDAuMDEsIDIuOTksIDEwMCkpCmBgYAoK0J/RgNC+0LLQtdGA0LrQsCDQvdCwINGB0YLQsNCx0LjQu9GM0L3QvtGB0YLRjCDQvNC+0LTQtdC70Lg6CgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpwbG90KHggPSAxOjUwLCB5ID0gc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSA1MCksIGNvbCA9ICJ3aGl0ZSIpCmZvciAoaiBpbiAxMToxOSl7CiAgbiA8LSAxOjUwCiAgcmVzIDwtIGMoKQogIGZvciAoaSBpbiBuKXsKICAgIGlmIChqIDw9IDEwKXsKICAgICAgcmVzIDwtIGMocmVzLCBoaXN0X21ha2UoaSwgYXMubnVtZXJpYyhtW2osXSksIGdldF9oaXN0ID0gRkFMU0UsICdiaW5vbWxvZycpWzRdKQogICAgfQogICAgZWxzZXsKICAgICAgcmVzIDwtIGMocmVzLCBoaXN0X21ha2UoaSwgYXMubnVtZXJpYyhtW2osXSksIGdldF9oaXN0ID0gRkFMU0UsICdsb2diaW5vbScpWzRdKQogICAgfQogIH0KICBsaW5lcyh4ID0gbiwgeSA9IHJlcywgY29sID0gaikKfQpgYGAKCtCa0L7RgNGA0LXQu9GP0YbQuNGPINGBINGA0LDQtNC40LDRhtC40LXQuSDQv9Cw0YDQsNC80LXRgtGA0L7QsiDQuCDRgdGA0LXQtNC90LXQs9C+OgoKYGBge3J9CnBsb3Qoc2VxKDAsIDQ1LCA1KSwgZGYuQkxSJHBbMToxMF0sIHR5cGUgPSAibCIsIGNvbCA9ICJibHVlIiwgeWxpbSA9IGMoMCwgbWF4KGRmLkJMUiRwKSksIHlsYWIgPSAiUGFyYW1ldHIgcCIsIHhsYWIgPSAiRG9zZSwgR3kiLCBtYWluID0gIkJsYWNrIC0gaW4gdml0cm87IEJsdWUgLSBpbiB2aXZvIikKbGluZXMoc2VxKDAsIDQwLCA1KSwgZGYuQkxSJHBbMTE6MTldLCB0eXBlID0gImIiLCBjb2wgPSAiYmxhY2siKQoKcGxvdChzZXEoMCwgNDUsIDUpLCBkZi5CTFIkcVsxOjEwXSwgdHlwZSA9ICJsIiwgY29sID0gImJsdWUiLCB5bGltID0gYygwLCBtYXgoZGYuQkxSJHEpKSwgeWxhYiA9ICJQYXJhbWV0ciBxIiwgeGxhYiA9ICJEb3NlLCBHeSIsIG1haW4gPSAiQmxhY2sgLSBpbiB2aXRybzsgQmx1ZSAtIGluIHZpdm8iKQpsaW5lcyhzZXEoMCwgNDAsIDUpLCBkZi5CTFIkcVsxMToxOV0sIHR5cGUgPSAiYiIsIGNvbCA9ICJibGFjayIpCgpwbG90KHNlcSgwLCA0NSwgNSksIGRmLkJMUiRuWzE6MTBdLCB0eXBlID0gImwiLCB5bGltID0gYygwLCBtYXgoZGYuQkxSKSksIHlsYWIgPSAi0J/QsNGA0LDQvNC10YLRgCBuIiwgeGxhYiA9ICLQlNC+0LfQsCDQvtCx0LvRg9GH0LXQvdC40Y8sINCT0YAiKQpsaW5lcyhzZXEoMCwgNDAsIDUpLCBkZi5CTFIkblsxMToxOV0sIGx0eSA9IDIpCmxlZ2VuZCh4ID0gImJvdHRvbXJpZ2h0IiwgbGVnZW5kID0gYygiaW4gdml2byIsICJpbiB2aXRybyIpLCBsdHkgPSBjKDEsIDIpLCBjb2wgPSAxLCBsd2QgPSAyKQpgYGAKCmBgYHtyfQpwbG90KHNlcSgwLCA0NSwgNSksIGRmLkxCUiRwWzE6MTBdLCB0eXBlID0gImwiLCBjb2wgPSAiYmx1ZSIsIHlsaW0gPSBjKDAsIG1heChkZi5MQlIkcCkpLCB5bGFiID0gIlBhcmFtZXRyIHAiLCB4bGFiID0gIkRvc2UsIEd5IiwgbWFpbiA9ICJCbGFjayAtIGluIHZpdHJvOyBCbHVlIC0gaW4gdml2byIpCmxpbmVzKHNlcSgwLCA0MCwgNSksIGRmLkxCUiRwWzExOjE5XSwgdHlwZSA9ICJiIiwgY29sID0gImJsYWNrIikKCnBsb3Qoc2VxKDAsIDQ1LCA1KSwgZGYuTEJSJHFbMToxMF0sIHR5cGUgPSAibCIsIGNvbCA9ICJibHVlIiwgeWxpbSA9IGMoMCwgbWF4KGRmLkxCUiRxKSksIHlsYWIgPSAiUGFyYW1ldHIgcSIsIHhsYWIgPSAiRG9zZSwgR3kiLCBtYWluID0gIkJsYWNrIC0gaW4gdml0cm87IEJsdWUgLSBpbiB2aXZvIikKbGluZXMoc2VxKDAsIDQwLCA1KSwgZGYuTEJSJHFbMTE6MTldLCB0eXBlID0gImIiLCBjb2wgPSAiYmxhY2siKQoKcGxvdChzZXEoMCwgNDUsIDUpLCBkZi5MQlIkblsxOjEwXSwgdHlwZSA9ICJsIiwgY29sID0gImJsdWUiLCB5bGltID0gYygwLCBtYXgoZGYuTEJSKSksIHlsYWIgPSAi0J/QsNGA0LDQvNC10YLRgCBuIiwgeGxhYiA9ICLQlNC+0LfQsCDQvtCx0LvRg9GH0LXQvdC40Y8sINCT0YAiLCBtYWluID0gIkJsYWNrIC0gaW4gdml0cm87IEJsdWUgLSBpbiB2aXZvIikKbGluZXMoc2VxKDAsIDQwLCA1KSwgZGYuTEJSJG5bMTE6MTldLCB0eXBlID0gImIiLCBjb2wgPSAiYmxhY2siKQpgYGAKCmBgYHtyfQpwbG90KHNlcSgwLCA0NSwgNSksIGRmLkxQUiRtZWFuX2xvZ1sxOjEwXSwgdHlwZSA9ICJsIiwgeWxpbSA9IGMoMC45LCBtYXgoYyhkZi5MUFIkbWVhbl9sb2dbMToxMF0sIGRmLkxQUiRtZWFuX2xvZ1sxMToxOV0pKSksIHlsYWIgPSAi0KHRgNC10LTQvdC10LUg0LvQvtCz0LDRgNC40YTQvNC40YfQtdGB0LrQvtCz0L4iLCB4bGFiID0gItCU0L7Qt9CwINC+0LHQu9GD0YfQtdC90LjRjywg0JPRgCIpCmxpbmVzKHNlcSgwLCA0MCwgNSksIGRmLkxQUiRtZWFuX2xvZ1sxMToxOV0sIGx0eSA9IDIpCmxlZ2VuZCh4ID0gInRvcHJpZ2h0IiwgbGVnZW5kID0gYygiaW4gdml2byIsICJpbiB2aXRybyIpLCBsdHkgPSBjKDEsIDIpLCBjb2wgPSAxLCBsd2QgPSAyKQoKcGxvdChzZXEoMCwgNDUsIDUpLCBkZi5MUFIkbGFtYmRhWzE6MTBdLCB0eXBlID0gImwiLCB5bGltID0gYygwLCBtYXgoZGYuTFBSJGxhbWJkYSkpLCB5bGFiID0gItCh0YDQtdC00L3QtdC1INC/0YPQsNGB0YHQvtC90L7QstGB0LrQvtCz0L4iLCB4bGFiID0gItCU0L7Qt9CwINC+0LHQu9GD0YfQtdC90LjRjywg0JPRgCIpCmxpbmVzKHNlcSgwLCA0MCwgNSksIGRmLkxQUiRsYW1iZGFbMTE6MTldLCBsdHkgPSAyKQpsZWdlbmQoeCA9ICJib3R0b21yaWdodCIsIGxlZ2VuZCA9IGMoImluIHZpdm8iLCAiaW4gdml0cm8iKSwgbHR5ID0gYygxLCAyKSwgY29sID0gMSwgbHdkID0gMikKYGBgCgpgYGB7cn0KcGxvdChzZXEoMCwgNDUsIDUpLCBkZi5OQiRxWzE6MTBdLCB0eXBlID0gImwiLCBjb2wgPSAiYmx1ZSIsIHlsaW0gPSBjKDAsIG1heChkZi5OQiRxKSksIHlsYWIgPSAiUGFyYW1ldHIgcSIsIHhsYWIgPSAiRG9zZSwgR3kiLCBtYWluID0gIkJsYWNrIC0gaW4gdml0cm87IEJsdWUgLSBpbiB2aXZvIikKbGluZXMoc2VxKDAsIDQwLCA1KSwgZGYuTkIkcVsxMToxOV0sIHR5cGUgPSAiYiIsIGNvbCA9ICJibGFjayIpCgpwbG90KHNlcSgwLCA0NSwgNSksIGRmLk5CJG5bMToxMF0sIHR5cGUgPSAibCIsIGNvbCA9ICJibHVlIiwgeWxpbSA9IGMoMCwgbWF4KGRmLk5CJG4pKSwgeWxhYiA9ICJQYXJhbWV0ciBuIiwgeGxhYiA9ICJEb3NlLCBHeSIsIG1haW4gPSAiQmxhY2sgLSBpbiB2aXRybzsgQmx1ZSAtIGluIHZpdm8iKQpsaW5lcyhzZXEoMCwgNDAsIDUpLCBkZi5OQiRuWzExOjE5XSwgdHlwZSA9ICJiIiwgY29sID0gImJsYWNrIikKCnBsb3Qoc2VxKDAsIDQ1LCA1KSwgZGYuTkJSJGxhbWJkYVsxOjEwXSwgdHlwZSA9ICJsIiwgY29sID0gImJsdWUiLCB5bGltID0gYygwLCBtYXgoZGYuTkJSJGxhbWJkYVsxOjEwXSkpLCB5bGFiID0gIlBhcmFtZXRyIGxhbWJkYSIsIHhsYWIgPSAiRG9zZSwgR3kiLCBtYWluID0gIkJsYWNrIC0gaW4gdml0cm87IEJsdWUgLSBpbiB2aXZvIikKbGluZXMoc2VxKDAsIDQwLCA1KSwgZGYuTkJSJGxhbWJkYVsxMToxOV0sIHR5cGUgPSAiYiIsIGNvbCA9ICJibGFjayIpCmBgYAoKIyMg0JDQvdCw0LvQuNC3INGC0LXQutGB0YLQvtCyCgrQl9Cw0LPRgNGD0LfQuNC8INC00LDQvdC90YvQtToKCmBgYHtyfQpXb3JkU2FtcGxlIDwtIHJlYWQuY3N2KCIuL291dHB1dC5jc3YiKQoKbnVtLndvcmQgPC0gMTAwMApgYGAKCiMjIyDQndC10LPQsNGC0LjQstC90YvQuSDQsdC40L3QvtC8CgrQoNCw0YHRgdGH0LjRgtCw0LXQvCDQv9Cw0YDQsNC80LXRgtGA0Ysg0LTQu9GPINC/0LXRgNCy0YvRhSAkMTAwMCQg0YHQu9C+0LI6CgpgYGB7cn0KZGYud29yZC5uYmlub20gPC0gZGF0YS5mcmFtZSgpCgpzdGFydF90aW1lIDwtIFN5cy50aW1lKCkKCmNsIDwtIG1ha2VDbHVzdGVyKDUsIHR5cGUgPSAiU09DSyIpCnJlZ2lzdGVyRG9TTk9XKGNsKQoKZGYud29yZC5uYmlub20gPC0gZm9yZWFjaChpID0gMTpudW0ud29yZCwgLmNvbWJpbmUgPSByYmluZCwgLmlub3JkZXIgPSBUUlVFKSAlZG9wYXIlIHsKICBoaXN0V29yZCA8LSBoaXN0KGFzLm51bWVyaWMoV29yZFNhbXBsZVtpLCBdKSwgcHJvYmFiaWxpdHkgPSBUUlVFLCBicmVha3MgPSBzZXEoLTAuNSwgbWF4KFdvcmRTYW1wbGVbaSwgXSkgKyAwLjUsIDEpLCB4bGltID0gYygtMC41LCBtYXgoV29yZFNhbXBsZVtpLCBdKSArIDAuNSksIHBsb3QgPSBGQUxTRSkKICByZXMgPC0gaGlzdF9tYWtlKDAsIGhpc3RXb3JkJGNvdW50cywgZ2V0X2hpc3QgPSBGQUxTRSwgZGlzdHIgPSAnbmJpbm9tJykKICBjKHJvdy5uYW1lcyhXb3JkU2FtcGxlKVtpXSwgcmVzKQp9Cgpjb2xuYW1lcyhkZi53b3JkLm5iaW5vbSkgPC0gYygibmFtZSIsICJuIiwgInEiLCAicF92YWx1ZSIpCmRmLndvcmQubmJpbm9tIDwtIGFzLmRhdGEuZnJhbWUoZGYud29yZC5uYmlub20pCmRmLndvcmQubmJpbm9tIDwtIGRmLndvcmQubmJpbm9tIHw+IG11dGF0ZShuID0gYXMubnVtZXJpYyhuKSwgcSA9IGFzLm51bWVyaWMocSksIHBfdmFsdWUgPSBhcy5udW1lcmljKHBfdmFsdWUpKQoKdGltZWRpZmYgPC0gZGlmZnRpbWUoU3lzLnRpbWUoKSxzdGFydF90aW1lKQpjYXQoItCg0LDRgdGH0ZHRgiDQt9Cw0L3Rj9C7OiAiLCB0aW1lZGlmZiwgdW5pdHModGltZWRpZmYpKQpgYGAKCtCi0L7Rh9C10YfQvdGL0Lkg0LPRgNCw0YTQuNC6INC/0LDRgNCw0LzQtdGC0YDQvtCyINC00LvRjyDQv9C+0LvRg9GH0LXQvdC90YvRhSDRgdC70L7QsjoKCmBgYHtyfQpkZi53b3JkLm5iaW5vbSB8PiBmaWx0ZXIobiA8IDIwKSB8PiBtdXRhdGUocF92YWx1ZV9ncm91cCA9IGFzLmZhY3RvcihpZmVsc2UocF92YWx1ZSA8IDAuMSwgMSwgMikpKSB8PgogIGdncGxvdChhZXMoeCA9IHEsIHkgPSBuLCBjb2xvciA9IHBfdmFsdWVfZ3JvdXApKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKIyMjINCR0LjQvdC+0LzQuNCw0LvRjNC90L4t0LvQvtCz0LDRgNC40YTQvNC40YfQtdGB0LrQvtC1INGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNC1CgrQoNCw0YHRgdGH0LjRgtCw0LXQvCDQv9Cw0YDQsNC80LXRgtGA0Ysg0LTQu9GPINC/0LXRgNCy0YvRhSAkMTAwMCQg0YHQu9C+0LI6CgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpkZi53b3JkLmJpbmxvZyA8LSBkYXRhLmZyYW1lKCkKCnN0YXJ0X3RpbWUgPC0gU3lzLnRpbWUoKQoKY2wgPC0gbWFrZUNsdXN0ZXIoNSwgdHlwZSA9ICJTT0NLIikKcmVnaXN0ZXJEb1NOT1coY2wpCgpkZi53b3JkLmJpbmxvZyA8LSBmb3JlYWNoKGkgPSAxOm51bS53b3JkLCAuY29tYmluZSA9IHJiaW5kLCAuaW5vcmRlciA9IFRSVUUpICVkb3BhciUgewogIHByaW50KGkpCiAgaGlzdFdvcmQgPC0gaGlzdChhcy5udW1lcmljKFdvcmRTYW1wbGVbaSwgXSksIHByb2JhYmlsaXR5ID0gVFJVRSwgYnJlYWtzID0gc2VxKC0wLjUsIG1heChXb3JkU2FtcGxlW2ksIF0pICsgMC41LCAxKSwgeGxpbSA9IGMoLTAuNSwgbWF4KFdvcmRTYW1wbGVbaSwgXSkgKyAwLjUpLCBwbG90ID0gRkFMU0UpCiAgcmVzIDwtIGhpc3RfbWFrZSgwLCBoaXN0V29yZCRjb3VudHMsIGdldF9oaXN0ID0gRkFMU0UsIGRpc3RyID0gJ2Jpbm9tbG9nJykKICBjKHJvdy5uYW1lcyhXb3JkU2FtcGxlKVtpXSwgcmVzKQp9Cgpjb2xuYW1lcyhkZi53b3JkLmJpbmxvZykgPC0gYygibmFtZSIsICJuIiwgInEiLCAicCIsICJwX3ZhbHVlIikKZGYud29yZC5iaW5sb2cgPC0gYXMuZGF0YS5mcmFtZShkZi53b3JkLmJpbmxvZykKZGYud29yZC5iaW5sb2cgPC0gZGYud29yZC5iaW5sb2cgfD4gbXV0YXRlKG4gPSBhcy5udW1lcmljKG4pLCBxID0gYXMubnVtZXJpYyhxKSwgcCA9IGFzLm51bWVyaWMocCksIHBfdmFsdWUgPSBhcy5udW1lcmljKHBfdmFsdWUpKQoKdGltZWRpZmYgPC0gZGlmZnRpbWUoU3lzLnRpbWUoKSxzdGFydF90aW1lKQpjYXQoItCg0LDRgdGH0ZHRgiDQt9Cw0L3Rj9C7OiAiLCB0aW1lZGlmZiwgdW5pdHModGltZWRpZmYpKQpgYGAKCiMjIyDQodCy0ZHRgNGC0LrQsCDQtNCy0YPRhSDQvtGC0YDQuNGG0LDRgtC10LvRjNC90YvRhSDQsdC40L3QvtC80L7QsgoK0J/RgNC+0LLQtdGA0LrQsCDQvtGG0LXQvdC60Lgg0LTQu9GPINGB0LLRkdGA0YLQutC4INC+0YLRgNC40YbQsNGC0LXQu9GM0L3Ri9GFINCx0LjQvdC+0LzQvtCyOgoKYGBge3Igd2FybmluZz1GQUxTRX0Kc2FtRE5CIDwtIHJkb3VibGVuYmlub20oMTAwMCwgMC44LCAzLCAwLjUsIDEwKQpoaXN0RE5CIDwtIGhpc3QoYXMubnVtZXJpYyhzYW1ETkIpLCBwcm9iYWJpbGl0eSA9IFRSVUUsIGJyZWFrcyA9IHNlcSgtMC41LCBtYXgoc2FtRE5CKSArIDAuNSwgMSksIHhsaW0gPSBjKC0wLjUsIG1heChzYW1ETkIpICsgMC41KSwgcGxvdCA9IFRSVUUpCnJlcyA8LSBoaXN0X21ha2UoMCwgYXMubnVtZXJpYyhoaXN0RE5CJGNvdW50cyksIGdldF9oaXN0ID0gVFJVRSwgJ2RvdWJsZW5iaW5vbScpCnJlcwpgYGAKCtCg0LDRgdGB0YfQuNGC0LDQtdC8INC/0LDRgNCw0LzQtdGC0YDRiyDQtNC70Y8g0L/QtdGA0LLRi9GFICQxMDAwJCDRgdC70L7QsjoKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmRmLndvcmQuZG91YmxlbmJpbm9tIDwtIGRhdGEuZnJhbWUoKQoKc3RhcnRfdGltZSA8LSBTeXMudGltZSgpCgpjbCA8LSBtYWtlQ2x1c3Rlcig1LCB0eXBlID0gIlNPQ0siKQpyZWdpc3RlckRvU05PVyhjbCkKCmRmLndvcmQuZG91YmxlbmJpbm9tIDwtIGZvcmVhY2goaSA9IDE6bnVtLndvcmQsIC5jb21iaW5lID0gcmJpbmQsIC5pbm9yZGVyID0gVFJVRSkgJWRvcGFyJSB7CiAgaGlzdFdvcmQgPC0gaGlzdChhcy5udW1lcmljKFdvcmRTYW1wbGVbaSwgXSksIHByb2JhYmlsaXR5ID0gVFJVRSwgYnJlYWtzID0gc2VxKC0wLjUsIG1heChXb3JkU2FtcGxlW2ksIF0pICsgMC41LCAxKSwgeGxpbSA9IGMoLTAuNSwgbWF4KFdvcmRTYW1wbGVbaSwgXSkgKyAwLjUpLCBwbG90ID0gRkFMU0UpCiAgcmVzIDwtIGhpc3RfbWFrZSgwLCBoaXN0V29yZCRjb3VudHMsIGdldF9oaXN0ID0gRkFMU0UsIGRpc3RyID0gJ2RvdWJsZW5iaW5vbScpCiAgYyhyb3cubmFtZXMoV29yZFNhbXBsZSlbaV0sIHJlcykKfQoKY29sbmFtZXMoZGYud29yZC5kb3VibGVuYmlub20pIDwtIGMoIm5hbWUiLCAicDEiLCAic2l6ZTEiLCAicDIiLCAic2l6ZTIiLCAicF92YWx1ZSIpCmRmLndvcmQuZG91YmxlbmJpbm9tIDwtIGFzLmRhdGEuZnJhbWUoZGYud29yZC5kb3VibGVuYmlub20pCmRmLndvcmQuZG91YmxlbmJpbm9tIDwtIGRmLndvcmQuZG91YmxlbmJpbm9tIHw+IG11dGF0ZShwMSA9IGFzLm51bWVyaWMocDEpLCBzaXplMSA9IGFzLm51bWVyaWMoc2l6ZTEpLCBwMiA9IGFzLm51bWVyaWMocDIpLCBzaXplMiA9IGFzLm51bWVyaWMoc2l6ZTIpLCBwX3ZhbHVlID0gYXMubnVtZXJpYyhwX3ZhbHVlKSkKCnRpbWVkaWZmIDwtIGRpZmZ0aW1lKFN5cy50aW1lKCksc3RhcnRfdGltZSkKY2F0KCLQoNCw0YHRh9GR0YIg0LfQsNC90Y/QuzogIiwgdGltZWRpZmYsIHVuaXRzKHRpbWVkaWZmKSkKYGBgCgojIyMg0JvQvtCz0LDRgNC40YTQvNC40YfQtdGB0LrQuC3Qv9GD0LDRgdGB0L7QvdC+0LLRgdC60L7QtSDRgNCw0YHQv9GA0LXQtNC10LvQtdC90LjQtQoK0KDQsNGB0YHRh9C40YLQsNC10Lwg0L/QsNGA0LDQvNC10YLRgNGLINC00LvRjyDQv9C10YDQstGL0YUgJDEwMDAkINGB0LvQvtCyOgoKYGBge3Igd2FybmluZz1GQUxTRX0KZGYud29yZC5sb2dwb2lzIDwtIGRhdGEuZnJhbWUoKQoKc3RhcnRfdGltZSA8LSBTeXMudGltZSgpCgpjbCA8LSBtYWtlQ2x1c3Rlcig1LCB0eXBlID0gIlNPQ0siKQpyZWdpc3RlckRvU05PVyhjbCkKCmRmLndvcmQubG9ncG9pcyA8LSBmb3JlYWNoKGkgPSAxOm51bS53b3JkLCAuY29tYmluZSA9IHJiaW5kLCAuaW5vcmRlciA9IFRSVUUpICVkb3BhciUgewogIGhpc3RXb3JkIDwtIGhpc3QoYXMubnVtZXJpYyhXb3JkU2FtcGxlW2ksIF0pLCBwcm9iYWJpbGl0eSA9IFRSVUUsIGJyZWFrcyA9IHNlcSgtMC41LCBtYXgoV29yZFNhbXBsZVtpLCBdKSArIDAuNSwgMSksIHhsaW0gPSBjKC0wLjUsIG1heChXb3JkU2FtcGxlW2ksIF0pICsgMC41KSwgcGxvdCA9IEZBTFNFKQogIHJlcyA8LSBoaXN0X21ha2UoMCwgaGlzdFdvcmQkY291bnRzLCBnZXRfaGlzdCA9IEZBTFNFLCBkaXN0ciA9ICdsb2dwb2lzJykKICBjKHJvdy5uYW1lcyhXb3JkU2FtcGxlKVtpXSwgcmVzKQp9Cgpjb2xuYW1lcyhkZi53b3JkLmxvZ3BvaXMpIDwtIGMoIm5hbWUiLCAibGFtYmRhIiwgInEiLCAicF92YWx1ZSIpCmRmLndvcmQubG9ncG9pcyA8LSBhcy5kYXRhLmZyYW1lKGRmLndvcmQubG9ncG9pcykKZGYud29yZC5sb2dwb2lzIDwtIGRmLndvcmQubG9ncG9pcyB8PiBtdXRhdGUobGFtYmRhID0gYXMubnVtZXJpYyhsYW1iZGEpLCBxID0gYXMubnVtZXJpYyhxKSwgcF92YWx1ZSA9IGFzLm51bWVyaWMocF92YWx1ZSkpCgp0aW1lZGlmZiA8LSBkaWZmdGltZShTeXMudGltZSgpLHN0YXJ0X3RpbWUpCmNhdCgi0KDQsNGB0YfRkdGCINC30LDQvdGP0Ls6ICIsIHRpbWVkaWZmLCB1bml0cyh0aW1lZGlmZikpCmBgYAoKIyMjINCc0L7QtNC40YTQuNC60LDRhtC40Y8gWXVsZS1TaW1vbgoK0JzQvtC00LXQu9C40YDQvtCy0LDQvdC40LUg0L7RgtGA0LjRhtCw0YLQtdC70YzQvdC+0LPQviDQsdC40L3QvtC80LAg0YEg0L/QsNGA0LDQvNC10YLRgNC+0LwgcHJvYiwg0YDQsNGB0L/RgNC10LTQtdC70ZHQvdC90L7QvCDQv9C+INC70L7Qs9Cw0YDQuNGE0LzRgzoKCmBgYHtyfQpyX3l1bGVfc2ltb25fbmJpbm9tIDwtIGZ1bmN0aW9uKG4sIHJvLCBzaXplKXsKICByZXMgPC0gYygpCiAgCiAgZm9yIChpIGluIHJubG9nKG4sIGxvZ19wcmVwZXJpbmcocm8pKSl7CiAgICByZXMgPC0gYyhyZXMsIHJuYmlub20oMSwgc2l6ZSwgZXhwKC1pKSkpCiAgfQogIAogIHJlcwp9CmBgYAoK0JLQtdGA0L7Rj9GC0L3QvtGB0YLQuCDRjdGC0L7Qs9C+INGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNGPINC4INCz0LjRgdGC0L7Qs9GA0LDQvNC80LA6CgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpnZXRfcHJvYl95dWxlX3NpbW9uX25iaW5vbSA8LSBmdW5jdGlvbihrLCBxID0gMC41LCBuID0gMSwgbWF4X251bSA9IDEwMDApewogIHJlcyA8LSAwCiAgCiAgZm9yKGkgaW4gMTptYXhfbnVtKXsKICAgIG5iIDwtIGRuYmlub20oaywgbiwgZXhwKC1pKSkKICAgIGlmIChpcy5uYW4obmIpIHwgbmIgPT0gMCl7CiAgICAgIGJyZWFrCiAgICB9CiAgICByZXMgPC0gcmVzICsgbmIgKiAoLXEgKiogaSAvIGxvZygxIC0gcSkgLyBpKQogIH0KICAKICByZXMKfQoKeXVsZV9zaW1vbi5xIDwtIDAuNzA3Cnl1bGVfc2ltb24ubiA8LSAyMC45OQpzYW1wLnl1bGVfc2ltb25fbmJpbm9tIDwtIHJfeXVsZV9zaW1vbl9uYmlub20oMTAwMDAsIHl1bGVfc2ltb24ucSwgeXVsZV9zaW1vbi5uKQpzYW1wLnl1bGVfc2ltb25fbmJpbm9tIDwtIHNhbXAueXVsZV9zaW1vbl9uYmlub21bc2FtcC55dWxlX3NpbW9uX25iaW5vbSA8IDIwMF0KaGlzdC5kZWZhdWx0KHNhbXAueXVsZV9zaW1vbl9uYmlub20sIGJyZWFrcyA9IHNlcSgtMC41LCBtYXgoc2FtcC55dWxlX3NpbW9uX25iaW5vbSkgKyAwLjUsIDEpLCBwcm9iYWJpbGl0eSA9IFRSVUUpCnBvaW50cyh4ID0gMDoyMDAsIHkgPSBzYXBwbHkoMDoyMDAsIGZ1bmN0aW9uKG4pIGdldF9wcm9iX3l1bGVfc2ltb25fbmJpbm9tKG4sIHl1bGVfc2ltb24ucSwgeXVsZV9zaW1vbi5uKSkpCmBgYAoK0KTRg9C90LrRhtC40Y8g0LzQsNC60YHQuNC80LDQu9GM0L3QvtCz0L4g0L/RgNCw0LLQtNC+0L/QvtC00L7QsdC40Y86CgpgYGB7cn0KbV9sb2dfbGlrX3l1bGVfc2ltb25fbmJpbm9tIDwtIGZ1bmN0aW9uKHguaW4sIHEgPSAwLjUsIG4gPSAxKXsKICBpZiAocSA8PSAwIHx8IHEgPj0gMSB8fCBuIDw9IDApewogICAgcmV0dXJuKC1sb2coMCkpOwogIH0KICByZXMgPC0gMAogIAogIGZvciAoaSBpbiB4LmluKXsKICAgIHByb2IgPC0gZ2V0X3Byb2JfeXVsZV9zaW1vbl9uYmlub20oaSwgcSwgbikKICAgIAogICAgaWYgKHByb2IgPCAwIHx8IHByb2IgPiAxIHx8IGlzLm5hbihwcm9iKSkKICAgICAgcmV0dXJuKC1sb2coMCkpCiAgICAKICAgIHJlcyA8LSByZXMgKyBsb2cocHJvYikKICB9CiAgCiAgLXJlcwp9CmBgYAoK0KDQsNGB0YHRh9C40YLQsNC10Lwg0L/QsNGA0LDQvNC10YLRgNGLINC00LvRjyDQv9C10YDQstGL0YUgJDEwMDAkINGB0LvQvtCyOgoKYGBge3J9CmRmLndvcmQueXVsZXNpbW9ubmJpbm9tIDwtIGRhdGEuZnJhbWUoKQoKc3RhcnRfdGltZSA8LSBTeXMudGltZSgpCgpjbCA8LSBtYWtlQ2x1c3Rlcig1LCB0eXBlID0gIlNPQ0siKQpyZWdpc3RlckRvU05PVyhjbCkKCmRmLndvcmQueXVsZXNpbW9ubmJpbm9tIDwtIGZvcmVhY2goaSA9IDE6bnVtLndvcmQsIC5jb21iaW5lID0gcmJpbmQsIC5pbm9yZGVyID0gVFJVRSkgJWRvcGFyJSB7CiAgaGlzdFdvcmQgPC0gaGlzdChhcy5udW1lcmljKFdvcmRTYW1wbGVbaSwgXSksIHByb2JhYmlsaXR5ID0gVFJVRSwgYnJlYWtzID0gc2VxKC0wLjUsIG1heChXb3JkU2FtcGxlW2ksIF0pICsgMC41LCAxKSwgeGxpbSA9IGMoLTAuNSwgbWF4KFdvcmRTYW1wbGVbaSwgXSkgKyAwLjUpLCBwbG90ID0gRkFMU0UpCiAgcmVzIDwtIGhpc3RfbWFrZSgwLCBoaXN0V29yZCRjb3VudHMsIGdldF9oaXN0ID0gRkFMU0UsIGRpc3RyID0gJ3l1bGVzaW1vbm5iaW5vbScpCiAgYyhyb3cubmFtZXMoV29yZFNhbXBsZSlbaV0sIHJlcykKfQoKY29sbmFtZXMoZGYud29yZC55dWxlc2ltb25uYmlub20pIDwtIGMoIm5hbWUiLCAicSIsICJuIiwgInBfdmFsdWUiKQpkZi53b3JkLnl1bGVzaW1vbm5iaW5vbSA8LSBhcy5kYXRhLmZyYW1lKGRmLndvcmQueXVsZXNpbW9ubmJpbm9tKQpkZi53b3JkLnl1bGVzaW1vbm5iaW5vbSA8LSBkZi53b3JkLnl1bGVzaW1vbm5iaW5vbSB8PiBtdXRhdGUocSA9IGFzLm51bWVyaWMocSksIG4gPSBhcy5udW1lcmljKG4pLCBwX3ZhbHVlID0gYXMubnVtZXJpYyhwX3ZhbHVlKSkKCnRpbWVkaWZmIDwtIGRpZmZ0aW1lKFN5cy50aW1lKCksc3RhcnRfdGltZSkKY2F0KCLQoNCw0YHRh9GR0YIg0LfQsNC90Y/QuzogIiwgdGltZWRpZmYsIHVuaXRzKHRpbWVkaWZmKSkKYGBgCgpgYGB7cn0KZGYud29yZC55dWxlc2ltb25uYmlub20gPC0gZGYud29yZC55dWxlc2ltb25uYmlub20gfD4gbXV0YXRlKHBfdmFsdWUgPSBpZmVsc2UocF92YWx1ZSA9PSAxLCAwLCBwX3ZhbHVlKSkKYGBgCgojIyMg0KDQsNC30L3QuNGG0LAg0LzQtdC20LTRgyDQvNC+0LTQtdC70Y/QvNC4CgrQk9C40YHRgtC+0LPRgNCw0LzQvNGLINGB0LvQvtCy0LAg0L/QviDQstGB0LXQvCDRgNCw0YHQv9C10YDQtNC10LvQtdC90LjRj9C8OgoKYGBge3Igd2FybmluZz1GQUxTRX0KbmFtZSA8LSAnY29hdCcKd29yZCA8LSBhcy5udW1lcmljKFdvcmRTYW1wbGVbbmFtZSwgXSkKaGlzdFdvcmQgPC0gaGlzdChhcy5udW1lcmljKHdvcmQpLCBwcm9iYWJpbGl0eSA9IFRSVUUsIGJyZWFrcyA9IHNlcSgtMC41LCBtYXgod29yZCkgKyAwLjUsIDEpLCB4bGltID0gYygtMC41LCBtYXgod29yZCkgKyAwLjUpLCBtYWluID0gIiIsIHhsYWIgPSAi0JfQvdCw0YfQtdC90LjRjyDQstGL0LHQvtGA0LrQuCIsIHlsYWIgPSAi0JLQtdGA0L7Rj9GC0L3QvtGB0YLQuCIpCnJlcyA8LSBoaXN0X21ha2UoMCwgaGlzdFdvcmQkY291bnRzLCBnZXRfaGlzdCA9IFRSVUUsIGRpc3RyID0gJ2Jpbm9tbG9nJykKcmVzCnJlcyA8LSBoaXN0X21ha2UoMCwgaGlzdFdvcmQkY291bnRzLCBnZXRfaGlzdCA9IFRSVUUsIGRpc3RyID0gJ2RvdWJsZW5iaW5vbScpCnJlcwpyZXMgPC0gaGlzdF9tYWtlKDAsIGhpc3RXb3JkJGNvdW50cywgZ2V0X2hpc3QgPSBUUlVFLCBkaXN0ciA9ICduYmlub20nKQpyZXMKcmVzIDwtIGhpc3RfbWFrZSgwLCBoaXN0V29yZCRjb3VudHMsIGdldF9oaXN0ID0gVFJVRSwgZGlzdHIgPSAnbG9ncG9pcycpCnJlcwojIHJlcyA8LSBoaXN0X21ha2UoMCwgaGlzdFdvcmQkY291bnRzLCBnZXRfaGlzdCA9IFRSVUUsIGRpc3RyID0gJ3l1bGVzaW1vbm5iaW5vbScpCiMgcmVzCmBgYAoK0KHQstC10LTRkdC8INGA0LXQt9GD0LvRjNGC0LDRgtGLINCyINC+0LTQvdGDINGC0LDQsdC70LjRhtGDOgoKYGBge3J9CmRmLndvcmQuYWxsIDwtIGNiaW5kKGRmLndvcmQubmJpbm9tLCBzZWxlY3QoZGYud29yZC5iaW5sb2csIC1uYW1lKSwgc2VsZWN0KGRmLndvcmQuZG91YmxlbmJpbm9tLCAtbmFtZSksIGZyZXEgPSBhcHBseShXb3JkU2FtcGxlLCAxLCBzdW0pWzE6MTAwMF0pCmNvbG5hbWVzKGRmLndvcmQuYWxsKSA8LSBjKCJuYW1lIiwgIm5fbmJpbm9tIiwgInFfbmJpbm9tIiwgInB2YWx1ZV9uYmlub20iLCAibl9iaW5vbWxvZyIsICJxX2Jpbm9tbG9nIiwgInBfYmlub21sb2ciLCAicHZhbHVlX2Jpbm9tbG9nIiwgInAxX2RvdWJsZW5iaW5vbSIsICJzaXplMV9kb3VibGVuYmlub20iLCAicDJfZG91YmxlbmJpbm9tIiwgInNpemUyX2RvdWJsZW5iaW5vbSIsICJwdmFsdWVfZG91YmxlbmJpbm9tIiwgImZyZXEiKQpgYGAKCtCX0LDQtNCw0LTQuNC8INGD0YDQvtCy0LXQvdGMINC30L3QsNGH0LjQvNC+0YHRgtC4OgoKYGBge3J9CmFscGhhIDwtIDAuMDUKYGBgCgrQlNC70Y8g0LrQsNC60LjRhSDRgdC70L7QsiDQstGB0LUg0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40Y8g0YXQvtGA0L7RiNC4OgoKYGBge3J9CmRmLndvcmQuYWxsIHw+IHNlbGVjdChuYW1lLCBwdmFsdWVfbmJpbm9tLCBwdmFsdWVfYmlub21sb2csIHB2YWx1ZV9kb3VibGVuYmlub20pIHw+IGZpbHRlcihwdmFsdWVfbmJpbm9tID4gYWxwaGEgJiBwdmFsdWVfYmlub21sb2cgPiBhbHBoYSAmIHB2YWx1ZV9kb3VibGVuYmlub20gPiBhbHBoYSkKYGBgCgoK0JTQu9GPINC60LDQutC40YUg0YHQu9C+0LIg0LvRg9GH0YjQtSDRgdCy0ZHRgNGC0LrQsCDQtNCy0YPRhSDQsdC40L3QvtC80L7QsjoKCmBgYHtyfQpkZi53b3JkLmFsbCB8PiBzZWxlY3QobmFtZSwgcHZhbHVlX25iaW5vbSwgcHZhbHVlX2Jpbm9tbG9nLCBwdmFsdWVfZG91YmxlbmJpbm9tKSB8PiBmaWx0ZXIocHZhbHVlX2RvdWJsZW5iaW5vbSA+IGFscGhhICYgcHZhbHVlX2Jpbm9tbG9nIDwgYWxwaGEgJiBwdmFsdWVfbmJpbm9tIDwgYWxwaGEpCmBgYAoK0JTQu9GPINC60LDQutC40YUg0YHQu9C+0LIg0JHQm9CgINC70YPRh9GI0LU6CgpgYGB7cn0KZGYud29yZC5hbGwgfD4gc2VsZWN0KG5hbWUsIHB2YWx1ZV9uYmlub20sIHB2YWx1ZV9iaW5vbWxvZywgcHZhbHVlX2RvdWJsZW5iaW5vbSkgfD4gZmlsdGVyKHB2YWx1ZV9iaW5vbWxvZyA+IGFscGhhICYgcHZhbHVlX2RvdWJsZW5iaW5vbSA8IGFscGhhICYgcHZhbHVlX25iaW5vbSA8IGFscGhhKQpgYGAKCtCU0LvRjyDQutCw0LrQuNGFINGB0LvQvtCyINC70YPRh9GI0LUg0L3QtdCz0LDRgtC40LLQvdGL0Lkg0LHQuNC90L7QvDoKCmBgYHtyfQpkZi53b3JkLmFsbCB8PiBzZWxlY3QobmFtZSwgcHZhbHVlX25iaW5vbSwgcHZhbHVlX2Jpbm9tbG9nLCBwdmFsdWVfZG91YmxlbmJpbm9tKSB8PiBmaWx0ZXIocHZhbHVlX25iaW5vbSA+IGFscGhhICYgcHZhbHVlX2RvdWJsZW5iaW5vbSA8IGFscGhhICYgcHZhbHVlX2Jpbm9tbG9nIDwgYWxwaGEpCmBgYAoK0JTQu9GPINC60LDQutC40YUg0YHQu9C+0LIg0LvRg9GH0YjQtSDQvtGC0YDQuNGG0LDRgtC10LvRjNC90YvQuSDQsdC40L3QvtC8INC4INCR0JvQoCwg0L3QviDQvdC1INGB0LLRkdGA0YLQutCwOgoKYGBge3J9CmRmLndvcmQuYWxsIHw+IHNlbGVjdChuYW1lLCBwdmFsdWVfbmJpbm9tLCBwdmFsdWVfYmlub21sb2csIHB2YWx1ZV9kb3VibGVuYmlub20pIHw+IGZpbHRlcihwdmFsdWVfZG91YmxlbmJpbm9tIDwgYWxwaGEgJiBwdmFsdWVfbmJpbm9tID4gYWxwaGEgJiBwdmFsdWVfYmlub21sb2cgPiBhbHBoYSkKYGBgCgrQlNC70Y8g0LrQsNC60LjRhSDRgdC70L7QsiDQu9GD0YfRiNC1INC+0YLRgNC40YbQsNGC0LXQu9GM0L3Ri9C5INCx0LjQvdC+0Lwg0Lgg0YHQstGR0YDRgtC60LAsINC90L4g0L3QtSDQkdCb0KA6CgpgYGB7cn0KZGYud29yZC5hbGwgfD4gc2VsZWN0KG5hbWUsIHB2YWx1ZV9uYmlub20sIHB2YWx1ZV9iaW5vbWxvZywgcHZhbHVlX2RvdWJsZW5iaW5vbSkgfD4gZmlsdGVyKHB2YWx1ZV9kb3VibGVuYmlub20gPiBhbHBoYSAmIHB2YWx1ZV9uYmlub20gPiBhbHBoYSAmIHB2YWx1ZV9iaW5vbWxvZyA8IGFscGhhKQpgYGAKCtCU0LvRjyDQutCw0LrQuNGFINGB0LvQvtCyINC70YPRh9GI0LUg0JHQm9CgINC4INGB0LLRkdGA0YLQutCwLCDQvdC+INC90LUg0L7RgtGA0LjRhtCw0YLQtdC70YzQvdGL0Lkg0LHQuNC90L7QvDoKCmBgYHtyfQpkZi53b3JkLmFsbCB8PiBzZWxlY3QobmFtZSwgcHZhbHVlX25iaW5vbSwgcHZhbHVlX2Jpbm9tbG9nLCBwdmFsdWVfZG91YmxlbmJpbm9tKSB8PiBmaWx0ZXIocHZhbHVlX2RvdWJsZW5iaW5vbSA+IGFscGhhICYgcHZhbHVlX25iaW5vbSA8IGFscGhhICYgcHZhbHVlX2Jpbm9tbG9nID4gYWxwaGEpCmBgYAoK0JTQu9GPINC60LDQutC40YUg0YHQu9C+0LIg0LLRgdC1INC/0LvQvtGF0Lg6CgpgYGB7cn0KZGYud29yZC5hbGwgfD4gc2VsZWN0KG5hbWUsIHB2YWx1ZV9uYmlub20sIHB2YWx1ZV9iaW5vbWxvZywgcHZhbHVlX2RvdWJsZW5iaW5vbSkgfD4gZmlsdGVyKHB2YWx1ZV9uYmlub20gPCBhbHBoYSAmIHB2YWx1ZV9kb3VibGVuYmlub20gPCBhbHBoYSAmIHB2YWx1ZV9iaW5vbWxvZyA8IGFscGhhKQpgYGAKCtCe0YLQtNC10LvRjNC90YvQtSDQutC70LDRgdGB0Ysg0LTQu9GPINGB0LvQvtCyOgoKYGBge3J9CmRmLndvcmQuYWxsIDwtIGRmLndvcmQuYWxsIHw+IAogIG11dGF0ZShjbGFzcyA9IGlmZWxzZShwdmFsdWVfZG91YmxlbmJpbm9tID4gYWxwaGEgJiBwdmFsdWVfbmJpbm9tID4gYWxwaGEgJiBwdmFsdWVfYmlub21sb2cgPiBhbHBoYSwgItCS0YHQtSIsICItIikpIHw+IAogIG11dGF0ZShjbGFzcyA9IGlmZWxzZShwdmFsdWVfZG91YmxlbmJpbm9tID4gYWxwaGEgJiBwdmFsdWVfbmJpbm9tIDwgYWxwaGEgJiBwdmFsdWVfYmlub21sb2cgPCBhbHBoYSwgItCh0YPQvNC80LAiLCBjbGFzcykpIHw+IAogIG11dGF0ZShjbGFzcyA9IGlmZWxzZShwdmFsdWVfZG91YmxlbmJpbm9tIDwgYWxwaGEgJiBwdmFsdWVfbmJpbm9tIDwgYWxwaGEgJiBwdmFsdWVfYmlub21sb2cgPiBhbHBoYSwgItCR0JvQoCIsIGNsYXNzKSkgfD4gCiAgbXV0YXRlKGNsYXNzID0gaWZlbHNlKHB2YWx1ZV9kb3VibGVuYmlub20gPCBhbHBoYSAmIHB2YWx1ZV9uYmlub20gPiBhbHBoYSAmIHB2YWx1ZV9iaW5vbWxvZyA8IGFscGhhLCAi0J7QkdCgIiwgY2xhc3MpKSB8PgogIG11dGF0ZShjbGFzcyA9IGlmZWxzZShwdmFsdWVfZG91YmxlbmJpbm9tID4gYWxwaGEgJiBwdmFsdWVfbmJpbm9tID4gYWxwaGEgJiBwdmFsdWVfYmlub21sb2cgPCBhbHBoYSwgItCe0JHQoCDQuCDRgdGD0LzQvNCwIiwgY2xhc3MpKSB8PgogIG11dGF0ZShjbGFzcyA9IGlmZWxzZShwdmFsdWVfZG91YmxlbmJpbm9tIDwgYWxwaGEgJiBwdmFsdWVfbmJpbm9tID4gYWxwaGEgJiBwdmFsdWVfYmlub21sb2cgPiBhbHBoYSwgItCe0JHQoCDQuCDQkdCb0KAiLCBjbGFzcykpIHw+CiAgbXV0YXRlKGNsYXNzID0gaWZlbHNlKHB2YWx1ZV9kb3VibGVuYmlub20gPiBhbHBoYSAmIHB2YWx1ZV9uYmlub20gPCBhbHBoYSAmIHB2YWx1ZV9iaW5vbWxvZyA+IGFscGhhLCAi0JHQm9CgINC4INGB0YPQvNC80LAiLCBjbGFzcykpIHw+CiAgbXV0YXRlKGNsYXNzID0gaWZlbHNlKHB2YWx1ZV9kb3VibGVuYmlub20gPCBhbHBoYSAmIHB2YWx1ZV9uYmlub20gPCBhbHBoYSAmIHB2YWx1ZV9iaW5vbWxvZyA8IGFscGhhLCAi0J3QuCDQvtC00LjQvSIsIGNsYXNzKSkKYGBgCgrQn9C+0YHQvNC+0YLRgNC40Lwg0L3QsCDRgtC+0YfQtdGH0L3Ri9C5INCz0YDQsNGE0LjQujoKCmBgYHtyfQpkZi53b3JkLmFsbCB8PiBwbG90X2x5KHggPSB+bG9nKG5fbmJpbm9tKSwgeSA9IH5xX25iaW5vbSwgeiA9IH5sb2coZnJlcSksIGNvbG9yID0gfmNsYXNzLCB0ZXh0ID0gfnBhc3RlKCd3b3JkOicsIG5hbWUpLCBzaXplID0gSSgxNTApLCBhbHBoYSA9IDEsICB3aWR0aCA9IDEyMDAsIGhlaWdodCA9IDcwMCkgfD4KICBsYXlvdXQoc2NlbmUgPSBsaXN0KHhheGlzID0gbGlzdCh0aXRsZSA9ICfQm9C+0LPQsNGA0LjRhNC8INC/0LDRgNCw0LzQtdGC0YDQsCBuINC90LXQs9Cw0YLQuNCy0L3QvtCz0L4g0LHQuNC90L7QvNCwJyksIHlheGlzID0gbGlzdCh0aXRsZSA9ICfQn9Cw0YDQsNC80LXRgtGAIHEg0L3QtdCz0LDRgtC40LLQvdC+0LPQviDQsdC40L3QvtC80LAnKSwgemF4aXMgPSBsaXN0KHRpdGxlID0gJ9Cb0L7Qs9Cw0YDQuNGE0Lwg0LrQvtC70LjRh9C10YHRgtCy0LAnKSkpCmBgYAo=